]> git.saurik.com Git - apple/xnu.git/blobdiff - bsd/kern/tty.c
xnu-4903.241.1.tar.gz
[apple/xnu.git] / bsd / kern / tty.c
index c03484ceca3b244e1a5727c15cdec88821d58953..ed7e7965820295c04f6079338897c2b896c34e96 100644 (file)
@@ -1,16 +1,19 @@
 /*
- * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
+ * Copyright (c) 1997-2017 Apple 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, 1990, 1991, 1993
  *      The Regents of the University of California.  All rights reserved.
  *     o Restore TS_WOPEN since it is useful in pstat.  It must be cleared
  *       only when _all_ openers leave open().
  */
-#ifdef NeXT
-#define NSNP           0
-#else
-#include "snp.h"
-#include "opt_uconsole.h"
-#endif
-
 #include <sys/param.h>
 #define        TTYDEFCHARS 1
 #include <sys/systm.h>
 #undef TTYDEFCHARS
 #include <sys/ioctl.h>
-#include <sys/proc.h>
-#include <sys/file.h>
+#include <sys/proc_internal.h>
+#include <sys/kauth.h>
+#include <sys/file_internal.h>
 #include <sys/conf.h>
 #include <sys/dkstat.h>
-#include <sys/uio.h>
+#include <sys/uio_internal.h>
 #include <sys/kernel.h>
 #include <sys/vnode.h>
 #include <sys/syslog.h>
 #include <sys/user.h>
 #include <sys/signalvar.h>
 #include <sys/signalvar.h>
-#ifndef NeXT
-#include <sys/resourcevar.h>
-#endif
 #include <sys/malloc.h>
-#if NSNP > 0
-#include <sys/snoop.h>
-#endif
 
-#ifndef NeXT
-#include <vm/vm.h>
-#include <vm/vm_param.h>
-#include <vm/vm_prot.h>
-#include <vm/lock.h>
-#include <vm/pmap.h>
-#include <vm/vm_map.h>
-#else
 #include <dev/kmreg_com.h>
 #include <machine/cons.h>
-#include <machine/spl.h>
-#if 0 /* [ */
-#include <machdep/machine/pmap.h>
-#endif  /* 0 ] */
-#endif /* !NeXT */
-
-#ifndef NeXT
-static int     proc_compare __P((struct proc *p1, struct proc *p2));
-#endif /* NeXT */
-static int     ttnread __P((struct tty *tp));
-static void    ttyecho __P((int c, struct tty *tp));
-static int     ttyoutput __P((int c, register struct tty *tp));
-static void    ttypend __P((struct tty *tp));
-static void    ttyretype __P((struct tty *tp));
-static void    ttyrub __P((int c, struct tty *tp));
-static void    ttyrubo __P((struct tty *tp, int cnt));
-static void    ttystop __P((struct tty *tp, int rw));
-static void    ttyunblock __P((struct tty *tp));
-static int     ttywflush __P((struct tty *tp));
+#include <sys/resource.h>      /* averunnable */
+#include <kern/waitq.h>
+#include <libkern/section_keywords.h>
+
+static lck_grp_t       *tty_lck_grp;
+static lck_grp_attr_t  *tty_lck_grp_attr;
+static lck_attr_t      *tty_lck_attr;
+
+__private_extern__ int ttnread(struct tty *tp);
+static void    ttyecho(int c, struct tty *tp);
+static int     ttyoutput(int c, struct tty *tp);
+static void    ttypend(struct tty *tp);
+static void    ttyretype(struct tty *tp);
+static void    ttyrub(int c, struct tty *tp);
+static void    ttyrubo(struct tty *tp, int count);
+static void    ttystop(struct tty *tp, int rw);
+static void    ttyunblock(struct tty *tp);
+static int     ttywflush(struct tty *tp);
+static int     proc_compare(proc_t p1, proc_t p2);
+
+void ttyhold(struct tty *tp);
+static void    ttydeallocate(struct tty *tp);
+
+static int isctty(proc_t p, struct tty  *tp);
+static int isctty_sp(proc_t p, struct tty  *tp, struct session *sessp);
 
 /*
  * Table with character classes and parity. The 8th bit indicates parity,
@@ -166,6 +154,8 @@ static int  ttywflush __P((struct tty *tp));
 
 #define        CCLASSMASK      0x3f
 #define        CCLASS(c)       (char_type[c] & CCLASSMASK)
+/* 0b10xxxxxx is the mask for UTF-8 continuations */
+#define        CCONT(c)        ((c & 0xc0) == 0x80)
 
 #define        BS      BACKSPACE
 #define        CC      CONTROL
@@ -236,23 +226,114 @@ static u_char const char_type[] = {
 #define        I_HIGH_WATER    (TTYHOG - 2 * 256)      /* XXX */
 #define        I_LOW_WATER     ((TTYHOG - 2 * 256) * 7 / 8)    /* XXX */
 
-#undef MAX_INPUT               /* XXX wrong in <sys/syslimits.h> */
-#define        MAX_INPUT       TTYHOG
+static void
+termios32to64(struct termios32 *in, struct user_termios *out)
+{
+       out->c_iflag = (user_tcflag_t)in->c_iflag;
+       out->c_oflag = (user_tcflag_t)in->c_oflag;
+       out->c_cflag = (user_tcflag_t)in->c_cflag;
+       out->c_lflag = (user_tcflag_t)in->c_lflag;
+
+       /* bcopy is OK, since this type is ILP32/LP64 size invariant */
+       bcopy(in->c_cc, out->c_cc, sizeof(in->c_cc));
+
+       out->c_ispeed = (user_speed_t)in->c_ispeed;
+       out->c_ospeed = (user_speed_t)in->c_ospeed;
+}
+
+static void
+termios64to32(struct user_termios *in, struct termios32 *out)
+{
+       out->c_iflag = (tcflag_t)in->c_iflag;
+       out->c_oflag = (tcflag_t)in->c_oflag;
+       out->c_cflag = (tcflag_t)in->c_cflag;
+       out->c_lflag = (tcflag_t)in->c_lflag;
+
+       /* bcopy is OK, since this type is ILP32/LP64 size invariant */
+       bcopy(in->c_cc, out->c_cc, sizeof(in->c_cc));
+
+       out->c_ispeed = (speed_t)in->c_ispeed;
+       out->c_ospeed = (speed_t)in->c_ospeed;
+}
+
+
+/*
+ * tty_init
+ *
+ * Initialize the tty line discipline subsystem.
+ *
+ * Parameters: void
+ *
+ * Returns:    void
+ *
+ * Locks:      No ttys can be allocated and no tty locks can be used
+ *             until after this function is called
+ *
+ * Notes:      The intent of this is to set up a log group attribute,
+ *             lock group, and loc atribute for subsequent per-tty locks.
+ *             This function is called early in bsd_init(), prior to the
+ *             console device initialization.
+ */
+void
+tty_init(void)
+{
+       tty_lck_grp_attr = lck_grp_attr_alloc_init();
+       tty_lck_grp = lck_grp_alloc_init("tty",  tty_lck_grp_attr);
+       tty_lck_attr = lck_attr_alloc_init();
+}
+
 
 /*
+ * tty_lock
+ *
+ * Lock the requested tty structure.
+ *
+ * Parameters: tp                              The tty we want to lock
+ *
+ * Returns:    void
+ *
+ * Locks:      On return, tp is locked
+ */
+void
+tty_lock(struct tty *tp)
+{
+       TTY_LOCK_NOTOWNED(tp);  /* debug assert */
+       lck_mtx_lock(&tp->t_lock);
+}
+
+
+/*
+ * tty_unlock
+ *
+ * Unlock the requested tty structure.
+ *
+ * Parameters: tp                              The tty we want to unlock
+ *
+ * Returns:    void
+ *
+ * Locks:      On return, tp is unlocked
+ */
+void
+tty_unlock(struct tty *tp)
+{
+       TTY_LOCK_OWNED(tp);     /* debug assert */
+       lck_mtx_unlock(&tp->t_lock);
+}
+
+/*
+ * ttyopen (LDISC)
+ *
  * Initial open of tty, or (re)entry to standard tty line discipline.
+ *
+ * Locks:      Assumes tty_lock() is held prior to calling.
  */
 int
-ttyopen(device, tp)
-       dev_t device;
-       register struct tty *tp;
+ttyopen(dev_t device, struct tty *tp)
 {
-       int s;
-       boolean_t funnel_state;
+       TTY_LOCK_OWNED(tp);     /* debug assert */
 
-       funnel_state = thread_funnel_set(kernel_flock, TRUE);
-       s = spltty();
        tp->t_dev = device;
+
        if (!ISSET(tp->t_state, TS_ISOPEN)) {
                SET(tp->t_state, TS_ISOPEN);
                if (ISSET(tp->t_cflag, CLOCAL)) {
@@ -260,75 +341,72 @@ ttyopen(device, tp)
                bzero(&tp->t_winsize, sizeof(tp->t_winsize));
        }
 
-#ifndef NeXT
-       /*
-        * Initialize or restore a cblock allocation policy suitable for
-        * the standard line discipline.
-        */
-       clist_alloc_cblocks(&tp->t_canq, TTYHOG, 512);
-       clist_alloc_cblocks(&tp->t_outq, TTMAXHIWAT + OBUFSIZ + 100,
-                           TTMAXHIWAT + OBUFSIZ + 100);
-       clist_alloc_cblocks(&tp->t_rawq, TTYHOG, TTYHOG);
-#endif /* !NeXT */
-
-       splx(s);
-       thread_funnel_set(kernel_flock, funnel_state);
        return (0);
 }
 
 /*
+ * ttyclose
+ *
  * Handle close() on a tty line: flush and set to initial state,
  * bumping generation number so that pending read/write calls
  * can detect recycling of the tty.
  * XXX our caller should have done `spltty(); l_close(); ttyclose();'
  * and l_close() should have flushed, but we repeat the spltty() and
  * the flush in case there are buggy callers.
+ *
+ * Locks:      Assumes tty_lock() is held prior to calling.
  */
 int
-ttyclose(tp)
-       register struct tty *tp;
+ttyclose(struct tty *tp)
 {
-       int s;
+       struct pgrp * oldpg;
+       struct session * oldsessp;
+       struct knote *kn;
+
+       TTY_LOCK_OWNED(tp);     /* debug assert */
 
-       s = spltty();
        if (constty == tp) {
                constty = NULL;
 
-splx(s);
-spltty();
 
-#ifdef NeXT
                /*
                 * Closing current console tty; disable printing of console
                 * messages at bottom-level driver. 
                 */
                (*cdevsw[major(tp->t_dev)].d_ioctl)
                        (tp->t_dev, KMIOCDISABLCONS, NULL, 0, current_proc());
-#endif /* NeXT */
        }
 
        ttyflush(tp, FREAD | FWRITE);
-#ifndef NeXT
-       clist_free_cblocks(&tp->t_canq);
-       clist_free_cblocks(&tp->t_outq);
-       clist_free_cblocks(&tp->t_rawq);
-#endif
-
-#if NSNP > 0
-       if (ISSET(tp->t_state, TS_SNOOP) && tp->t_sc != NULL)
-               snpdown((struct snoop *)tp->t_sc);
-#endif
 
        tp->t_gen++;
        tp->t_line = TTYDISC;
+       proc_list_lock();
+       oldpg = tp->t_pgrp;
+       oldsessp = tp->t_session;
        tp->t_pgrp = NULL;
        tp->t_session = NULL;
+       if (oldsessp != SESSION_NULL)
+               oldsessp->s_ttypgrpid = NO_PID;
+       proc_list_unlock();
+       /* drop the reference on prev session and pgrp */
+       /* SAFE: All callers drop the lock on return */
+       tty_unlock(tp);
+       if (oldsessp != SESSION_NULL)
+               session_rele(oldsessp);
+       if (oldpg != PGRP_NULL)
+               pg_rele(oldpg);
+       tty_lock(tp);
        tp->t_state = 0;
-#if NeXT
+       SLIST_FOREACH(kn, &tp->t_wsel.si_note, kn_selnext) {
+               KNOTE_DETACH(&tp->t_wsel.si_note, kn);
+       }
        selthreadclear(&tp->t_wsel);
+       SLIST_FOREACH(kn, &tp->t_rsel.si_note, kn_selnext) {
+               KNOTE_DETACH(&tp->t_rsel.si_note, kn);
+       }
        selthreadclear(&tp->t_rsel);
-#endif
-       splx(s);
+
        return (0);
 }
 
@@ -344,19 +422,26 @@ spltty();
         (c) != _POSIX_VDISABLE))
 
 /*
+ * ttyinput (LDISC)
+ *
  * Process input of a single character received on a tty.
+ *
+ * Parameters: c                       The character received
+ *             tp                      The tty on which it was received
+ *
+ * Returns:    .
+ *
+ * Locks:      Assumes tty_lock() is held prior to calling.
  */
 int
-ttyinput(c, tp)
-       register int c;
-       register struct tty *tp;
+ttyinput(int c, struct tty *tp)
 {
-       register tcflag_t iflag, lflag;
-       register cc_t *cc;
-       int i, err, retval;
-       boolean_t funnel_state;
+       tcflag_t iflag, lflag;
+       cc_t *cc;
+       int i, err;
+       int retval = 0;                 /* default return value */
 
-       funnel_state = thread_funnel_set(kernel_flock, TRUE);
+       TTY_LOCK_OWNED(tp);     /* debug assert */
         
        /*
         * If input is pending take it first.
@@ -396,12 +481,14 @@ ttyinput(c, tp)
                CLR(c, TTY_ERRORMASK);
                if (ISSET(err, TTY_BI)) {
                        if (ISSET(iflag, IGNBRK)) {
-                               thread_funnel_set(kernel_flock, funnel_state);
-                               return (0);
+                               goto out;
                         }
                        if (ISSET(iflag, BRKINT)) {
                                ttyflush(tp, FREAD | FWRITE);
-                               pgsignal(tp->t_pgrp, SIGINT, 1);
+                               /* SAFE: All callers drop the lock on return */
+                               tty_unlock(tp);
+                               tty_pgsignal(tp, SIGINT, 1);
+                               tty_lock(tp);
                                goto endcase;
                        }
                        if (ISSET(iflag, PARMRK))
@@ -409,8 +496,7 @@ ttyinput(c, tp)
                } else if ((ISSET(err, TTY_PE) && ISSET(iflag, INPCK))
                        || ISSET(err, TTY_FE)) {
                        if (ISSET(iflag, IGNPAR)) {
-                               thread_funnel_set(kernel_flock, funnel_state);
-                               return (0);
+                               goto out;
                        }
                        else if (ISSET(iflag, PARMRK)) {
 parmrk:
@@ -481,15 +567,29 @@ parmrk:
                                if (!ISSET(lflag, NOFLSH))
                                        ttyflush(tp, FREAD | FWRITE);
                                ttyecho(c, tp);
-                               pgsignal(tp->t_pgrp,
+                               /*
+                                * SAFE: All callers drop the lock on return;
+                                * SAFE: if we lose a threaded race on change
+                                * SAFE: of the interrupt character, we could
+                                * SAFE: have lost that race anyway due to the
+                                * SAFE: scheduler executing threads in
+                                * SAFE: priority order rather than "last
+                                * SAFE: active thread" order (FEATURE).
+                                */
+                               tty_unlock(tp);
+                               tty_pgsignal(tp,
                                    CCEQ(cc[VINTR], c) ? SIGINT : SIGQUIT, 1);
+                               tty_lock(tp);
                                goto endcase;
                        }
                        if (CCEQ(cc[VSUSP], c)) {
                                if (!ISSET(lflag, NOFLSH))
                                        ttyflush(tp, FREAD);
                                ttyecho(c, tp);
-                               pgsignal(tp->t_pgrp, SIGTSTP, 1);
+                               /* SAFE: All callers drop the lock on return */
+                               tty_unlock(tp);
+                               tty_pgsignal(tp, SIGTSTP, 1);
+                               tty_lock(tp);
                                goto endcase;
                        }
                }
@@ -501,12 +601,10 @@ parmrk:
                                if (!ISSET(tp->t_state, TS_TTSTOP)) {
                                        SET(tp->t_state, TS_TTSTOP);
                                         ttystop(tp, 0);
-                                       thread_funnel_set(kernel_flock, funnel_state);
-                                       return (0);
+                                       goto out;
                                }
                                if (!CCEQ(cc[VSTART], c)) {
-                                       thread_funnel_set(kernel_flock, funnel_state);
-                                       return (0);
+                                       goto out;
                                 }
                                /*
                                 * if VSTART == VSTOP then toggle
@@ -521,8 +619,7 @@ parmrk:
                 */
                if (c == '\r') {
                        if (ISSET(iflag, IGNCR)) {
-                               thread_funnel_set(kernel_flock, funnel_state);
-                               return (0);
+                               goto out;
                         }
                        else if (ISSET(iflag, ICRNL))
                                c = '\n';
@@ -538,8 +635,15 @@ parmrk:
                 * erase (^H / ^?)
                 */
                if (CCEQ(cc[VERASE], c)) {
-                       if (tp->t_rawq.c_cc)
-                               ttyrub(unputc(&tp->t_rawq), tp);
+                       if (tp->t_rawq.c_cc) {
+                               if (ISSET(iflag, IUTF8)) {
+                                       do {
+                                               ttyrub((c = unputc(&tp->t_rawq)), tp);
+                                       } while(tp->t_rawq.c_cc && CCONT(c));
+                               } else {
+                                       ttyrub(unputc(&tp->t_rawq), tp);
+                               }
+                       }
                        goto endcase;
                }
                /*
@@ -612,10 +716,14 @@ parmrk:
                 * ^T - kernel info and generate SIGINFO
                 */
                if (CCEQ(cc[VSTATUS], c) && ISSET(lflag, IEXTEN)) {
-                       if (ISSET(lflag, ISIG))
-                               pgsignal(tp->t_pgrp, SIGINFO, 1);
+                       if (ISSET(lflag, ISIG)) {
+                               /* SAFE: All callers drop the lock on return */
+                               tty_unlock(tp);
+                               tty_pgsignal(tp, SIGINFO, 1);
+                               tty_lock(tp);
+                       }
                        if (!ISSET(lflag, NOKERNINFO))
-                               ttyinfo(tp);
+                               ttyinfo_locked(tp);
                        goto endcase;
                }
        }
@@ -671,37 +779,52 @@ input_overflow:
                        }
                }
        }
+
 endcase:
        /*
         * IXANY means allow any character to restart output.
         */
        if (ISSET(tp->t_state, TS_TTSTOP) &&
            !ISSET(iflag, IXANY) && cc[VSTART] != cc[VSTOP]) {
-               thread_funnel_set(kernel_flock, funnel_state);
-               return (0);
+               goto out;
         }
+
 restartoutput:
        CLR(tp->t_lflag, FLUSHO);
        CLR(tp->t_state, TS_TTSTOP);
+
 startoutput:
-    retval = ttstart(tp);
-       thread_funnel_set(kernel_flock, funnel_state);
+       /* Start the output */
+       retval = ttstart(tp);
+
+out:
        return (retval);
 }
 
+
 /*
+ * ttyoutput
+ *
  * Output a single character on a tty, doing output processing
  * as needed (expanding tabs, newline processing, etc.).
- * Returns < 0 if succeeds, otherwise returns char to resend.
- * Must be recursive.
+ *
+ * Parameters: c                       The character to output
+ *             tp                      The tty on which to output on the tty
+ *
+ * Returns:    < 0                     Success
+ *             >= 0                    Character to resend (failure)
+ *
+ * Locks:      Assumes tp is locked on entry, remains locked on exit
+ *
+ * Notes:      Must be recursive.
  */
 static int
-ttyoutput(c, tp)
-       register int c;
-       register struct tty *tp;
+ttyoutput(int c, struct tty *tp)
 {
-       register tcflag_t oflag;
-       register int col, s;
+       tcflag_t oflag;
+       int col;
+
+       TTY_LOCK_OWNED(tp);     /* debug assert */
 
        oflag = tp->t_oflag;
        if (!ISSET(oflag, OPOST)) {
@@ -722,16 +845,14 @@ ttyoutput(c, tp)
        CLR(c, ~TTY_CHARMASK);
        if (c == '\t' &&
            ISSET(oflag, OXTABS) && !ISSET(tp->t_lflag, EXTPROC)) {
-               c = 8 - (tp->t_column & 7);
+               col = c = 8 - (tp->t_column & 7);
                if (!ISSET(tp->t_lflag, FLUSHO)) {
-                       s = spltty();           /* Don't interrupt tabs. */
-                       c -= b_to_q("        ", c, &tp->t_outq);
+                       c -= b_to_q((const u_char *)"        ", c, &tp->t_outq);
                        tk_nout += c;
                        tp->t_outcc += c;
-                       splx(s);
                }
                tp->t_column += c;
-               return (c ? -1 : '\t');
+               return (c == col ? -1 : '\t');
        }
        if (c == CEOT && ISSET(oflag, ONOEOT))
                return (-1);
@@ -746,6 +867,12 @@ ttyoutput(c, tp)
                if (putc('\r', &tp->t_outq))
                        return (c);
        }
+        /* If OCRNL is set, translate "\r" into "\n". */
+        else if (c == '\r' && ISSET(tp->t_oflag, OCRNL))
+                c = '\n';
+        /* If ONOCR is set, don't transmit CRs when on column 0. */
+        else if (c == '\r' && ISSET(tp->t_oflag, ONOCR) && tp->t_column == 0)
+                return (-1);
        tk_nout++;
        tp->t_outcc++;
        if (!ISSET(tp->t_lflag, FLUSHO) && putc(c, &tp->t_outq))
@@ -775,47 +902,162 @@ ttyoutput(c, tp)
 }
 
 /*
- * Ioctls for all tty devices.  Called after line-discipline specific ioctl
- * has been called to do discipline-specific functions and/or reject any
- * of these ioctl commands.
+ * Sets the tty state to not allow any more changes of foreground process
+ * group. This is required to be done so that a subsequent revoke on a vnode
+ * is able to always successfully complete.
+ *
+ * Locks :   Assumes tty_lock held on entry
+ */
+void
+ttysetpgrphup(struct tty *tp)
+{
+       TTY_LOCK_OWNED(tp);     /* debug assert */
+       SET(tp->t_state, TS_PGRPHUP);
+       /*
+        * Also wake up sleeping readers which may or may not belong to the
+        * current foreground process group.
+        *
+        * This forces any non-fg readers (which entered read when
+        * that process group was in the fg) to return with EIO (if they're
+        * catching SIGTTIN or with SIGTTIN). The ones which do belong to the fg
+        * process group will promptly go back to sleep and get a SIGHUP shortly
+        * This would normally happen as part of the close in revoke but if
+        * there is a sleeping reader from a non-fg process group we never get
+        * to the close because the sleeping reader holds an iocount on the
+        * vnode of the terminal which is going to get revoked->reclaimed.
+        */
+       wakeup(TSA_HUP_OR_INPUT(tp));
+}
+
+/*
+ * Locks : Assumes tty lock held on entry
+ */
+void
+ttyclrpgrphup(struct tty *tp)
+{
+       TTY_LOCK_OWNED(tp);     /* debug assert */
+       CLR(tp->t_state, TS_PGRPHUP);
+}
+
+/*
+ * ttioctl
+ *
+ * Identical to ttioctl_locked, only the lock is not held
+ *
+ * Parameters: <See ttioctl_locked()>
+ *
+ * Returns:    <See ttioctl_locked()>
+ *
+ * Locks:      This function assumes the tty_lock() is not held on entry;
+ *             it takes the lock, and releases it before returning.
+ *
+ * Notes:      This is supported to ensure the line discipline interfaces
+ *             all have the same locking semantics.
+ *
+ *             This function is called from 
  */
-/* ARGSUSED */
 int
-#ifndef NeXT
-ttioctl(tp, cmd, data, flag)
-       register struct tty *tp;
-       int cmd, flag;
-       void *data;
-#else
-ttioctl(tp, cmd, data, flag, p)
-       register struct tty *tp;
-       u_long cmd;
-       caddr_t data;
-       int flag;
-       struct proc *p;
-#endif
+ttioctl(struct tty *tp, u_long cmd, caddr_t data, int flag, proc_t p)
 {
-#ifndef NeXT
-       register struct proc *p = curproc;      /* XXX */
-#endif
-       int s, error;
+       int     retval;
+
+       tty_lock(tp);
+       retval = ttioctl_locked(tp, cmd, data, flag, p);
+       tty_unlock(tp);
+
+       return (retval);
+}
+
+
+/*
+ * ttioctl_locked
+ *
+ * Ioctls for all tty devices.
+ *
+ * Parameters: tp                      Tty on which ioctl() is being called
+ *             cmd                     ioctl() command parameter
+ *             data                    ioctl() data argument (if any)
+ *             flag                    fileglob open modes from fcntl.h;
+ *                                     if called internally, this is usually
+ *                                     set to 0, rather than something useful
+ *             p                       Process context for the call; if the
+ *                                     call is proxied to a worker thread,
+ *                                     this will not be the current process!!!
+ *
+ * Returns:    0                       Success
+ *             EIO                     I/O error (no process group, job
+ *                                     control, etc.)
+ *             EINTR                   Interrupted by signal
+ *             EBUSY                   Attempt to become the console while
+ *                                     the console is busy
+ *             ENOTTY                  TIOCGPGRP on a non-controlling tty
+ *             EINVAL                  Invalid baud rate
+ *             ENXIO                   TIOCSETD of invalid line discipline
+ *             EPERM                   TIOCSTI, not root, not open for read
+ *             EACCES                  TIOCSTI, not root, not your controlling
+ *                                     tty
+ *             EPERM                   TIOCSCTTY failed
+ *             ENOTTY/EINVAL/EPERM     TIOCSPGRP failed
+ *             EPERM                   TIOCSDRAINWAIT as non-root user
+ *     suser:EPERM                     Console control denied
+ *     ttywait:EIO                     t_timeout too small/expired
+ *     ttywait:ERESTART                Upper layer must redrive the call;
+ *                                     this is usually done by the Libc
+ *                                     stub in user space
+ *     ttywait:EINTR                   Interrupted (usually a signal)
+ *     ttcompat:EINVAL
+ *     ttcompat:ENOTTY
+ *     ttcompat:EIOCTL
+ *     ttcompat:ENOTTY                 TIOCGSID, if no session or session
+ *                                     leader
+ *     ttcompat:ENOTTY                 All unrecognized ioctls
+ *     *tp->t_param:?                  TIOCSETA* underlying function
+ *     *linesw[t].l_open:?             TIOCSETD line discipline open failure
+ *
+ *
+ * Locks:      This function assumes that the tty_lock() is held for the
+ *             tp at the time of the call.  The lock remains held on return.
+ *
+ * Notes:      This function is called after line-discipline specific ioctl
+ *             has been called to do discipline-specific functions and/or
+ *             reject any of these ioctl() commands.
+ *
+ *             This function calls ttcompat(), which can re-call ttioctl()
+ *             to a depth of one (FORTRAN style mutual recursion); at some
+ *             point, we should just in-line ttcompat() here.
+ */
+int
+ttioctl_locked(struct tty *tp, u_long cmd, caddr_t data, int flag, proc_t p)
+{
+       int error = 0;
+       int bogusData = 1;
        struct uthread *ut;
+       struct pgrp *pg, *oldpg;
+       struct session *sessp, *oldsessp;
+       struct tty *oldtp;
+
+       TTY_LOCK_OWNED(tp);     /* debug assert */
 
-       ut = (struct uthread *)get_bsdthread_info(current_act());
-       /* If the ioctl involves modification, hang if in the background. */
+       ut = (struct uthread *)get_bsdthread_info(current_thread());
+       /* If the ioctl involves modification, signal if in the background. */
        switch (cmd) {
+       case TIOCIXON:
+       case TIOCIXOFF:
+       case  TIOCDRAIN:
        case  TIOCFLUSH:
-       case  TIOCSETA:
+       case TIOCSTOP:
+       case TIOCSTART:
+       case  TIOCSETA_32:
+       case  TIOCSETA_64:
        case  TIOCSETD:
-       case  TIOCSETAF:
-       case  TIOCSETAW:
-#ifdef notdef
+       case  TIOCSETAF_32:
+       case  TIOCSETAF_64:
+       case  TIOCSETAW_32:
+       case  TIOCSETAW_64:
        case  TIOCSPGRP:
-#endif
        case  TIOCSTAT:
        case  TIOCSTI:
        case  TIOCSWINSZ:
-#if defined(COMPAT_43) || defined(COMPAT_SUNOS)
        case  TIOCLBIC:
        case  TIOCLBIS:
        case  TIOCLSET:
@@ -824,45 +1066,57 @@ ttioctl(tp, cmd, data, flag, p)
        case  TIOCSETN:
        case  TIOCSETP:
        case  TIOCSLTC:
-#endif
                while (isbackground(p, tp) &&
-                   (p->p_flag & P_PPWAIT) == 0 &&
+                   (p->p_lflag & P_LPPWAIT) == 0 &&
                    (p->p_sigignore & sigmask(SIGTTOU)) == 0 &&
                    (ut->uu_sigmask & sigmask(SIGTTOU)) == 0) {
-                       if (p->p_pgrp->pg_jobc == 0)
-                               return (EIO);
-                       pgsignal(p->p_pgrp, SIGTTOU, 1);
-                       error = ttysleep(tp, &lbolt, TTOPRI | PCATCH | PTTYBLOCK, "ttybg1",
-                                        0);
-                       if (error)
-                               return (error);
+                       pg = proc_pgrp(p);
+                       if (pg == PGRP_NULL) {
+                               error = EIO;
+                               goto out;
+                       }
+                       /* SAFE: All callers drop the lock on return */
+                       tty_unlock(tp);
+                       if (pg->pg_jobc == 0) {
+                               pg_rele(pg);
+                               tty_lock(tp);
+                               error = EIO;
+                               goto out;
+                       }
+                       pgsignal(pg, SIGTTOU, 1);
+                       pg_rele(pg);
+                       tty_lock(tp);
+
+
+                       /*
+                        * We signalled ourself, so we need to act as if we
+                        * have been "interrupted" from a "sleep" to act on
+                        * the signal.  If it's a signal that stops the
+                        * process, that's handled in the signal sending code.
+                        */
+                       error = EINTR;
+                       goto out;
                }
                break;
        }
 
        switch (cmd) {                  /* Process the ioctl. */
        case FIOASYNC:                  /* set/clear async i/o */
-               s = spltty();
                if (*(int *)data)
                        SET(tp->t_state, TS_ASYNC);
                else
                        CLR(tp->t_state, TS_ASYNC);
-               splx(s);
                break;
        case FIONBIO:                   /* set/clear non-blocking i/o */
                break;                  /* XXX: delete. */
        case FIONREAD:                  /* get # bytes to read */
-               s = spltty();
                *(int *)data = ttnread(tp);
-               splx(s);
                break;
        case TIOCEXCL:                  /* set exclusive use of tty */
-               s = spltty();
                SET(tp->t_state, TS_XCLUDE);
-               splx(s);
                break;
        case TIOCFLUSH: {               /* flush buffers */
-               register int flags = *(int *)data;
+               int flags = *(int *)data;
 
                if (flags == 0)
                        flags = FREAD | FWRITE;
@@ -871,51 +1125,53 @@ ttioctl(tp, cmd, data, flag, p)
                ttyflush(tp, flags);
                break;
        }
-#ifdef NeXT
        case TIOCSCONS: {
                /* Set current console device to this line */
-               int bogusData = 1;
                data = (caddr_t) &bogusData;
 
                /* No break - Fall through to BSD code */
        }
-#endif /* NeXT */
        case TIOCCONS: {                        /* become virtual console */
                if (*(int *)data) {
                        if (constty && constty != tp &&
                            ISSET(constty->t_state, TS_CONNECTED)) {
-                               return (EBUSY);
+                               error = EBUSY;
+                               goto out;
                        }
-#if defined(NeXT) || !defined(UCONSOLE)
-                       if ( (error = suser(p->p_ucred, &p->p_acflag)) )
-                               return (error);
-#endif
+                       if ( (error = suser(kauth_cred_get(), &p->p_acflag)) ) 
+                               goto out;
                        constty = tp;
                } else if (tp == constty) {
                        constty = NULL;
                }
-#ifdef NeXT
                if (constty) {
-                       (*cdevsw[major(cons.t_dev)].d_ioctl)
-                               (cons.t_dev, KMIOCDISABLCONS, NULL, 0, p);
+                       (*cdevsw[major(constty->t_dev)].d_ioctl)
+                               (constty->t_dev, KMIOCDISABLCONS, NULL, 0, p);
                } else {
                        (*cdevsw[major(tp->t_dev)].d_ioctl)
                                (tp->t_dev, KMIOCDISABLCONS, NULL, 0, p);
                }
-#endif /* NeXT */
                break;
        }
        case TIOCDRAIN:                 /* wait till output drained */
                error = ttywait(tp);
                if (error)
-                       return (error);
+                       goto out;
                break;
-       case TIOCGETA: {                /* get termios struct */
-               struct termios *t = (struct termios *)data;
-
-               bcopy(&tp->t_termios, t, sizeof(struct termios));
+       case TIOCGETA_32:               /* get termios struct */
+#ifdef __LP64__
+               termios64to32((struct user_termios *)&tp->t_termios, (struct termios32 *)data);
+#else
+               bcopy(&tp->t_termios, data, sizeof(struct termios));
+#endif
+               break;
+       case TIOCGETA_64:               /* get termios struct */
+#ifdef __LP64__
+               bcopy(&tp->t_termios, data, sizeof(struct termios));
+#else
+               termios32to64((struct termios32 *)&tp->t_termios, (struct user_termios *)data);
+#endif
                break;
-       }
        case TIOCGETD:                  /* get line discipline */
                *(int *)data = tp->t_line;
                break;
@@ -923,40 +1179,60 @@ ttioctl(tp, cmd, data, flag, p)
                *(struct winsize *)data = tp->t_winsize;
                break;
        case TIOCGPGRP:                 /* get pgrp of tty */
-               if (!isctty(p, tp))
-                       return (ENOTTY);
+               if (!isctty(p, tp)) {
+                       error = ENOTTY;
+                       goto out;
+               }
                *(int *)data = tp->t_pgrp ? tp->t_pgrp->pg_id : NO_PID;
                break;
 #ifdef TIOCHPCL
        case TIOCHPCL:                  /* hang up on last close */
-               s = spltty();
                SET(tp->t_cflag, HUPCL);
-               splx(s);
                break;
 #endif
        case TIOCNXCL:                  /* reset exclusive use of tty */
-               s = spltty();
                CLR(tp->t_state, TS_XCLUDE);
-               splx(s);
                break;
        case TIOCOUTQ:                  /* output queue size */
                *(int *)data = tp->t_outq.c_cc;
                break;
-       case TIOCSETA:                  /* set termios struct */
-       case TIOCSETAW:                 /* drain output, set */
-       case TIOCSETAF: {               /* drn out, fls in, set */
-               register struct termios *t = (struct termios *)data;
-
-               if (t->c_ispeed < 0 || t->c_ospeed < 0)
-                       return (EINVAL);
-               s = spltty();
-               if (cmd == TIOCSETAW || cmd == TIOCSETAF) {
+       case TIOCSETA_32:                       /* set termios struct */
+       case TIOCSETA_64:
+       case TIOCSETAW_32:                      /* drain output, set */
+       case TIOCSETAW_64:
+       case TIOCSETAF_32:              /* drn out, fls in, set */
+       case TIOCSETAF_64:
+       {               /* drn out, fls in, set */
+               struct termios *t = (struct termios *)data;
+               struct termios lcl_termios;
+
+#ifdef __LP64__
+               if (cmd==TIOCSETA_32 || cmd==TIOCSETAW_32 || cmd==TIOCSETAF_32) {
+                       termios32to64((struct termios32 *)data, (struct user_termios *)&lcl_termios);
+                       t = &lcl_termios;
+               }
+#else
+               if (cmd==TIOCSETA_64 || cmd==TIOCSETAW_64 || cmd==TIOCSETAF_64) {
+                       termios64to32((struct user_termios *)data, (struct termios32 *)&lcl_termios);
+                       t = &lcl_termios;
+               }
+#endif
+#if 0
+       /* XXX bogus test; always false */
+               if (t->c_ispeed < 0 || t->c_ospeed < 0) {
+                       error = EINVAL;
+                       goto out;
+               }
+#endif /* 0 - leave in; may end up being a conformance issue */
+               if (t->c_ispeed == 0)
+                       t->c_ispeed = t->c_ospeed;
+               if (cmd == TIOCSETAW_32 || cmd == TIOCSETAF_32 ||
+                   cmd == TIOCSETAW_64 || cmd == TIOCSETAF_64) {
                        error = ttywait(tp);
                        if (error) {
-                               splx(s);
-                               return (error);
+                               goto out;
                        }
-                       if (cmd == TIOCSETAF)
+                       if (cmd == TIOCSETAF_32 || cmd == TIOCSETAF_64)
                                ttyflush(tp, FREAD);
                }
                if (!ISSET(t->c_cflag, CIGNORE)) {
@@ -964,8 +1240,7 @@ ttioctl(tp, cmd, data, flag, p)
                         * Set device hardware.
                         */
                        if (tp->t_param && (error = (*tp->t_param)(tp, t))) {
-                               splx(s);
-                               return (error);
+                               goto out;
                        }
                        if (ISSET(t->c_cflag, CLOCAL) &&
                            !ISSET(tp->t_cflag, CLOCAL)) {
@@ -993,7 +1268,7 @@ ttioctl(tp, cmd, data, flag, p)
                        ttsetwater(tp);
                }
                if (ISSET(t->c_lflag, ICANON) != ISSET(tp->t_lflag, ICANON) &&
-                   cmd != TIOCSETAF) {
+                   cmd != TIOCSETAF_32 && cmd != TIOCSETAF_64) {
                        if (ISSET(t->c_lflag, ICANON))
                                SET(tp->t_lflag, PENDIN);
                        else {
@@ -1003,18 +1278,6 @@ ttioctl(tp, cmd, data, flag, p)
                                 * discipline.  Now we have to worry about
                                 * panicing for a null queue.
                                 */
-#ifndef NeXT
-                               if (tp->t_canq.c_cbreserved > 0 &&
-                                   tp->t_rawq.c_cbreserved > 0) {
-                                       catq(&tp->t_rawq, &tp->t_canq);
-                                       /*
-                                        * XXX the queue limits may be
-                                        * different, so the old queue
-                                        * swapping method no longer works.
-                                        */
-                                       catq(&tp->t_canq, &tp->t_rawq);
-                               }
-#else
                                if (tp->t_rawq.c_cs && tp->t_canq.c_cs) {
                                    struct clist tq;
 
@@ -1023,7 +1286,6 @@ ttioctl(tp, cmd, data, flag, p)
                                    tp->t_rawq = tp->t_canq;
                                    tp->t_canq = tq;
                                }
-#endif /* !NeXT */
                                CLR(tp->t_lflag, PENDIN);
                        }
                        ttwakeup(tp);
@@ -1042,184 +1304,335 @@ ttioctl(tp, cmd, data, flag, p)
                    t->c_cc[VTIME] != tp->t_cc[VTIME])
                        ttwakeup(tp);
                bcopy(t->c_cc, tp->t_cc, sizeof(t->c_cc));
-               splx(s);
                break;
        }
        case TIOCSETD: {                /* set line discipline */
-               register int t = *(int *)data;
+               int t = *(int *)data;
                dev_t device = tp->t_dev;
-               extern int nlinesw;
 
-               if ((u_int)t >= nlinesw)
-                       return (ENXIO);
+               if (t >= nlinesw || t < 0) {
+                       error = ENXIO;
+                       goto out;
+               }
+               /*
+                * If the new line discipline is not equal to the old one,
+                * close the old one and open the new one.
+                */
                if (t != tp->t_line) {
-                       s = spltty();
                        (*linesw[tp->t_line].l_close)(tp, flag);
                        error = (*linesw[t].l_open)(device, tp);
                        if (error) {
+                               /* This is racy; it's possible to lose both */
                                (void)(*linesw[tp->t_line].l_open)(device, tp);
-                               splx(s);
-                               return (error);
+                               goto out;
                        }
                        tp->t_line = t;
-                       splx(s);
                }
                break;
        }
        case TIOCSTART:                 /* start output, like ^Q */
-               s = spltty();
                if (ISSET(tp->t_state, TS_TTSTOP) ||
                    ISSET(tp->t_lflag, FLUSHO)) {
                        CLR(tp->t_lflag, FLUSHO);
                        CLR(tp->t_state, TS_TTSTOP);
                        ttstart(tp);
                }
-               splx(s);
                break;
        case TIOCSTI:                   /* simulate terminal input */
-               if (p->p_ucred->cr_uid && (flag & FREAD) == 0)
-                       return (EPERM);
-               if (p->p_ucred->cr_uid && !isctty(p, tp))
-                       return (EACCES);
-               s = spltty();
+               if (suser(kauth_cred_get(), NULL) && (flag & FREAD) == 0) {
+                       error = EPERM;
+                       goto out;
+               }
+               if (suser(kauth_cred_get(), NULL) && !isctty(p, tp)) {
+                       error = EACCES;
+                       goto out;
+               }
                (*linesw[tp->t_line].l_rint)(*(u_char *)data, tp);
-               splx(s);
                break;
        case TIOCSTOP:                  /* stop output, like ^S */
-               s = spltty();
                if (!ISSET(tp->t_state, TS_TTSTOP)) {
                        SET(tp->t_state, TS_TTSTOP);
                         ttystop(tp, 0);
                }
-               splx(s);
+               break;
+       case TIOCIXON:
+               ttyunblock(tp);
+               break;
+       case TIOCIXOFF:
+               ttyblock(tp);
                break;
        case TIOCSCTTY:                 /* become controlling tty */
                /* Session ctty vnode pointer set in vnode layer. */
-               if (!SESS_LEADER(p) ||
-                   ((p->p_session->s_ttyvp || tp->t_session) &&
-                   (tp->t_session != p->p_session)))
-                       return (EPERM);
-               tp->t_session = p->p_session;
-               tp->t_pgrp = p->p_pgrp;
-               p->p_session->s_ttyp = tp;
-               p->p_flag |= P_CONTROLT;
-               /* The backgrounded process blocking on tty now 
-                * could be foregound process. Wake such processes
+               sessp = proc_session(p);
+               if (sessp == SESSION_NULL) {
+                       error = EPERM;
+                       goto out;
+               }
+
+               /*
+                * This can only be done by a session leader.
+                */
+               if (!SESS_LEADER(p, sessp)) {
+                       /* SAFE: All callers drop the lock on return */
+                       tty_unlock(tp);
+                       session_rele(sessp);
+                       tty_lock(tp);
+                       error = EPERM;
+                       goto out;
+               }
+               /*
+                * If this terminal is already the controlling terminal for the
+                * session, nothing to do here.
+                */
+               if (tp->t_session == sessp) {
+                       /* SAFE: All callers drop the lock on return */
+                       tty_unlock(tp);
+                       session_rele(sessp);
+                       tty_lock(tp);
+                       error = 0;
+                       goto out;
+               }
+               pg = proc_pgrp(p);
+               /*
+                * Deny if the terminal is already attached to another session or
+                * the session already has a terminal vnode.
                 */
-               tty_pgsignal(tp->t_pgrp, SIGCONT);
+               session_lock(sessp);
+               if (sessp->s_ttyvp || tp->t_session) {
+                       session_unlock(sessp);
+                       /* SAFE: All callers drop the lock on return */
+                       tty_unlock(tp);
+                       if (pg != PGRP_NULL) {
+                               pg_rele(pg);
+                       }
+                       session_rele(sessp);
+                       tty_lock(tp);
+                       error = EPERM;
+                       goto out;
+               }
+               sessp->s_ttypgrpid = pg->pg_id;
+               oldtp = sessp->s_ttyp;
+               ttyhold(tp);
+               sessp->s_ttyp = tp;
+               session_unlock(sessp);
+               proc_list_lock();
+               oldsessp = tp->t_session;
+               oldpg = tp->t_pgrp;
+               if (oldsessp != SESSION_NULL)
+                       oldsessp->s_ttypgrpid = NO_PID;
+               /* do not drop refs on sessp and pg as tp holds them */
+               tp->t_session = sessp;
+               tp->t_pgrp = pg;
+               proc_list_unlock();
+               OSBitOrAtomic(P_CONTROLT, &p->p_flag);
+               /* SAFE: All callers drop the lock on return */
+               tty_unlock(tp);
+               /* drop the reference on prev session and pgrp */
+               if (oldsessp != SESSION_NULL)
+                       session_rele(oldsessp);
+               if (oldpg != PGRP_NULL)
+                       pg_rele(oldpg);
+               if (NULL != oldtp)
+                       ttyfree(oldtp);
+               tty_lock(tp);
                break;
+
        case TIOCSPGRP: {               /* set pgrp of tty */
-               register struct pgrp *pgrp = pgfind(*(int *)data);
+               struct pgrp *pgrp = PGRP_NULL;
 
-               if (!isctty(p, tp))
-                       return (ENOTTY);
-               else if (pgrp == NULL || pgrp->pg_session != p->p_session)
-                       return (EPERM);
+               sessp = proc_session(p);
+               if (!isctty_sp(p, tp, sessp)) {
+                       if (sessp != SESSION_NULL)
+                               session_rele(sessp);
+                       error = ENOTTY;
+                       goto out;
+               }
+               else if ((pgrp = pgfind(*(int *)data)) == PGRP_NULL) {
+                       if (sessp != SESSION_NULL)
+                               session_rele(sessp);
+                       error = EINVAL;
+                       goto out;
+                } else if (pgrp->pg_session != sessp) {
+                       /* SAFE: All callers drop the lock on return */
+                       tty_unlock(tp);
+                       if (sessp != SESSION_NULL)
+                               session_rele(sessp);
+                       pg_rele(pgrp);
+                       tty_lock(tp);
+                       error = EPERM;
+                       goto out;
+               }
+               /*
+                * The session leader is going away and is possibly going to revoke
+                * the terminal, we can't change the process group when that is the
+                * case.
+                */
+               if (ISSET(tp->t_state, TS_PGRPHUP)) {
+                       if (sessp != SESSION_NULL)
+                               session_rele(sessp);
+                       pg_rele(pgrp);
+                       error = EPERM;
+                       goto out;
+               }
+               proc_list_lock();
+               oldpg = tp->t_pgrp;
                tp->t_pgrp = pgrp;
-               /* The backgrounded process blocking on tty now 
-                * could be foregound process. Wake such processes
+               sessp->s_ttypgrpid = pgrp->pg_id;
+               proc_list_unlock();
+
+               /*
+                * Wakeup readers to recheck if they are still the foreground
+                * process group.
+                *
+                * ttwakeup() isn't called because the readers aren't getting
+                * woken up becuse there is something to read but to force
+                * the re-evaluation of their foreground process group status.
+                *
+                * Ordinarily leaving these readers waiting wouldn't be an issue
+                * as launchd would send them a termination signal eventually
+                * (if nobody else does). But if this terminal happens to be
+                * /dev/console, launchd itself could get blocked forever behind
+                * a revoke of /dev/console and leave the system deadlocked.
                 */
-               tty_pgsignal(tp->t_pgrp, SIGCONT);
+               wakeup(TSA_HUP_OR_INPUT(tp));
+
+               /* SAFE: All callers drop the lock on return */
+               tty_unlock(tp);
+               if (oldpg != PGRP_NULL)
+                       pg_rele(oldpg);
+               if (sessp != SESSION_NULL)
+                       session_rele(sessp);
+               tty_lock(tp);
                break;
        }
        case TIOCSTAT:                  /* simulate control-T */
-               s = spltty();
-               ttyinfo(tp);
-               splx(s);
+               ttyinfo_locked(tp);
                break;
        case TIOCSWINSZ:                /* set window size */
                if (bcmp((caddr_t)&tp->t_winsize, data,
                    sizeof (struct winsize))) {
                        tp->t_winsize = *(struct winsize *)data;
-                       pgsignal(tp->t_pgrp, SIGWINCH, 1);
+                       /* SAFE: All callers drop the lock on return */
+                       tty_unlock(tp);
+                       tty_pgsignal(tp, SIGWINCH, 1);
+                       tty_lock(tp);
                }
                break;
        case TIOCSDRAINWAIT:
-               error = suser(p->p_ucred, &p->p_acflag);
-               if (error)
-                       return (error);
+               error = suser(kauth_cred_get(), &p->p_acflag);
+               if (error) {
+                       goto out;
+               }
                tp->t_timeout = *(int *)data * hz;
                wakeup(TSA_OCOMPLETE(tp));
                wakeup(TSA_OLOWAT(tp));
                break;
        case TIOCGDRAINWAIT:
                *(int *)data = tp->t_timeout / hz;
-               break;
+               break; 
        default:
-#if defined(COMPAT_43) || defined(COMPAT_SUNOS)
-#ifdef NeXT
-               return (ttcompat(tp, cmd, data, flag, p));
-#else
-               return (ttcompat(tp, cmd, data, flag));
-#endif /* NeXT */
-#else
-               return (-1);
-#endif
+               error = ttcompat(tp, cmd, data, flag, p);
+               goto out;
        }
 
-       return (0);
+       error = 0;
+out:
+       return(error);
 }
 
+
+/*
+ * Locks:      Assumes tp is locked on entry, remains locked on exit
+ */
 int
-ttyselect(tp, rw, wql, p)
-       struct tty *tp;
-       int rw;
-       void * wql;
-       struct proc *p;
+ttyselect(struct tty *tp, int rw, void *wql, proc_t p)
 {
-       int s;
+       int retval = 0;
+       /*
+        * Attaching knotes to TTYs needs to call selrecord in order to hook
+        * up the waitq to the selinfo, regardless of data being ready.  See
+        * filt_ttyattach.
+        */
+       bool needs_selrecord = rw & FMARK;
+       rw &= ~FMARK;
 
-       if (tp == NULL)
-               return (ENXIO);
+       if (tp == NULL) {
+               return ENXIO;
+       }
+
+       TTY_LOCK_OWNED(tp);
+
+       if (tp->t_state & TS_ZOMBIE) {
+               retval = 1;
+               goto out;
+       }
 
-       s = spltty();
        switch (rw) {
        case FREAD:
-               if (ttnread(tp) > 0 || ISSET(tp->t_state, TS_ZOMBIE))
-                       goto win;
+               retval = ttnread(tp);
+               if (retval > 0) {
+                       break;
+               }
+
                selrecord(p, &tp->t_rsel, wql);
                break;
        case FWRITE:
-               if ((tp->t_outq.c_cc <= tp->t_lowat &&
-                    ISSET(tp->t_state, TS_CONNECTED))
-                   || ISSET(tp->t_state, TS_ZOMBIE)) {
-win:                   splx(s);
-                       return (1);
+               if ((tp->t_outq.c_cc <= tp->t_lowat) &&
+                   (tp->t_state & TS_CONNECTED)) {
+                       retval = tp->t_hiwat - tp->t_outq.c_cc;
+                       break;
                }
+
                selrecord(p, &tp->t_wsel, wql);
                break;
        }
-       splx(s);
-       return (0);
+
+out:
+       if (retval > 0 && needs_selrecord) {
+               switch (rw) {
+               case FREAD:
+                       selrecord(p, &tp->t_rsel, wql);
+                       break;
+               case FWRITE:
+                       selrecord(p, &tp->t_wsel, wql);
+                       break;
+               }
+       }
+
+       return retval;
 }
 
+
 /*
  * This is a wrapper for compatibility with the select vector used by
  * cdevsw.  It relies on a proper xxxdevtotty routine.
+ *
+ * Locks:      Assumes tty_lock() is not held prior to calling.
  */
 int
-ttselect(dev, rw, wql, p)
-       dev_t dev;
-       int rw;
-       void * wql;
-       struct proc *p;
+ttselect(dev_t dev, int rw, void *wql, proc_t p)
 {
-#ifndef NeXT
-       return ttyselect((*cdevsw[major(dev)]->d_devtotty)(dev), rw, wql, p);
-#else
-       return ttyselect(cdevsw[major(dev)].d_ttys[minor(dev)], rw, wql, p);
-#endif
-}
+       int     rv;
+       struct tty *tp = cdevsw[major(dev)].d_ttys[minor(dev)];
+
+       tty_lock(tp);
+       rv =  ttyselect(tp, rw, wql, p);
+       tty_unlock(tp);
+
+       return (rv);
+}
+
 
 /*
- * Must be called at spltty().
+ * Locks:      Assumes tp is locked on entry, remains locked on exit
  */
-static int
-ttnread(tp)
-       struct tty *tp;
+__private_extern__ int
+ttnread(struct tty *tp)
 {
        int nread;
 
+       TTY_LOCK_OWNED(tp);     /* debug assert */
+
        if (ISSET(tp->t_lflag, PENDIN))
                ttypend(tp);
        nread = tp->t_canq.c_cc;
@@ -1231,17 +1644,33 @@ ttnread(tp)
        return (nread);
 }
 
+
 /*
+ * ttywait
+ *
  * Wait for output to drain.
+ *
+ * Parameters: tp                      Tty on which to wait for output to drain
+ *
+ * Returns:    0                       Success
+ *             EIO                     t_timeout too small/expired
+ *     ttysleep:ERESTART               Upper layer must redrive the call;
+ *                                     this is usually done by the Libc
+ *                                     stub in user space
+ *     ttysleep:EINTR                  Interrupted (usually a signal)
+ *
+ * Notes:      Called from proc_exit() and vproc_exit().
+ *
+ * Locks:      Assumes tp is locked on entry, remains locked on exit
  */
 int
-ttywait(tp)
-       register struct tty *tp;
+ttywait(struct tty *tp)
 {
-       int error, s;
+       int error;
+
+       TTY_LOCK_OWNED(tp);     /* debug assert */
 
        error = 0;
-       s = spltty();
        while ((tp->t_outq.c_cc || ISSET(tp->t_state, TS_BUSY)) &&
               ISSET(tp->t_state, TS_CONNECTED) && tp->t_oproc) {
                (*tp->t_oproc)(tp);
@@ -1261,33 +1690,34 @@ ttywait(tp)
        }
        if (!error && (tp->t_outq.c_cc || ISSET(tp->t_state, TS_BUSY)))
                error = EIO;
-       splx(s);
        return (error);
 }
 
+/*
+ * Stop the underlying device driver.
+ *
+ * Locks:      Assumes tty_lock() is held prior to calling.
+ */
 static void
-ttystop(tp, rw)
-        struct tty *tp;
-        int rw;
+ttystop(struct tty *tp, int rw)
 {
-#ifdef sun4c                                           /* XXX */
-       (*tp->t_stop)(tp, rw);
-#elif defined(NeXT)
+       TTY_LOCK_OWNED(tp);     /* debug assert */
+
        (*cdevsw[major(tp->t_dev)].d_stop)(tp, rw);
-#else
-       (*cdevsw[major(tp->t_dev)]->d_stop)(tp, rw);
-#endif
 }
 
 /*
  * Flush if successfully wait.
+ *
+ * Locks:      Assumes tty_lock() is held prior to calling.
  */
 static int
-ttywflush(tp)
-       struct tty *tp;
+ttywflush(struct tty *tp)
 {
        int error;
 
+       TTY_LOCK_OWNED(tp);     /* debug assert */
+
        if ((error = ttywait(tp)) == 0)
                ttyflush(tp, FREAD);
        return (error);
@@ -1295,15 +1725,14 @@ ttywflush(tp)
 
 /*
  * Flush tty read and/or write queues, notifying anyone waiting.
+ *
+ * Locks:      Assumes tty_lock() is held prior to calling.
  */
 void
-ttyflush(tp, rw)
-       register struct tty *tp;
-       int rw;
+ttyflush(struct tty *tp, int rw)
 {
-       register int s;
+       TTY_LOCK_OWNED(tp);     /* debug assert */
 
-       s = spltty();
 #if 0
 again:
 #endif
@@ -1361,40 +1790,33 @@ again:
                FLUSHQ(&tp->t_outq);
                ttwwakeup(tp);
        }
-       splx(s);
 }
 
 /*
  * Copy in the default termios characters.
+ *
+ * Locks:      Assumes tty_lock() is held prior to calling.
+ *
+ * Notes:      No assertion; tp is not in scope.
  */
 void
-termioschars(t)
-       struct termios *t;
+termioschars(struct termios *t)
 {
-
        bcopy(ttydefchars, t->c_cc, sizeof t->c_cc);
 }
 
-/*
- * Old interface.
- */
-void
-ttychars(tp)
-       struct tty *tp;
-{
-
-       termioschars(&tp->t_termios);
-}
 
 /*
  * Handle input high water.  Send stop character for the IXOFF case.  Turn
  * on our input flow control bit and propagate the changes to the driver.
  * XXX the stop character should be put in a special high priority queue.
+ *
+ * Locks:      Assumes tty_lock() is held for the call.
  */
 void
-ttyblock(tp)
-       struct tty *tp;
+ttyblock(struct tty *tp)
 {
+       TTY_LOCK_OWNED(tp);     /* debug assert */
 
        SET(tp->t_state, TS_TBLOCK);
        if (ISSET(tp->t_iflag, IXOFF) && tp->t_cc[VSTOP] != _POSIX_VDISABLE &&
@@ -1403,15 +1825,18 @@ ttyblock(tp)
        ttstart(tp);
 }
 
+
 /*
  * Handle input low water.  Send start character for the IXOFF case.  Turn
  * off our input flow control bit and propagate the changes to the driver.
  * XXX the start character should be put in a special high priority queue.
+ *
+ * Locks:      Assumes tty_lock() is held for the call.
  */
 static void
-ttyunblock(tp)
-       struct tty *tp;
+ttyunblock(struct tty *tp)
 {
+       TTY_LOCK_OWNED(tp);     /* debug assert */
 
        CLR(tp->t_state, TS_TBLOCK);
        if (ISSET(tp->t_iflag, IXOFF) && tp->t_cc[VSTART] != _POSIX_VDISABLE &&
@@ -1420,76 +1845,69 @@ ttyunblock(tp)
        ttstart(tp);
 }
 
-#if defined(NeXT) || defined(notyet)
-/* FreeBSD: Not used by any current (i386) drivers. */
+
 /*
- * Restart after an inter-char delay.
+ * ttstart
+ *
+ * Start tty output
+ *
+ * Parameters: tp                      tty on which to start output
+ *
+ * Returns:    0                       Success
+ *
+ * Locks:      Assumes tty_lock() is held for the call.
+ *
+ * Notes:      This function might as well be void; it always returns success
+ *
+ *             Called from ttioctl_locked(), LDISC routines, and
+ *             ttycheckoutq(), ttyblock(), ttyunblock(), and tputchar()
  */
-void
-ttrstrt(tp_arg)
-       void *tp_arg;
-{
-       struct tty *tp;
-       int s;
-
-#if DIAGNOSTIC
-       if (tp_arg == NULL)
-               panic("ttrstrt");
-#endif
-       tp = tp_arg;
-       s = spltty();
-
-       CLR(tp->t_state, TS_TIMEOUT);
-       ttstart(tp);
-
-       splx(s);
-}
-#endif /* NeXT || notyet */
-
 int
-ttstart(tp)
-       struct tty *tp;
+ttstart(struct tty *tp)
 {
-       boolean_t funnel_state;
-
-       funnel_state = thread_funnel_set(kernel_flock, TRUE);
+       TTY_LOCK_OWNED(tp);     /* debug assert */
 
        if (tp->t_oproc != NULL)        /* XXX: Kludge for pty. */
                (*tp->t_oproc)(tp);
-       thread_funnel_set(kernel_flock, funnel_state);
+
        return (0);
 }
 
+
 /*
+ * ttylclose (LDISC)
+ *
  * "close" a line discipline
+ *
+ * Locks:      Assumes tty_lock() is held prior to calling.
  */
 int
-ttylclose(tp, flag)
-       struct tty *tp;
-       int flag;
+ttylclose(struct tty *tp, int flag)
 {
-       boolean_t funnel_state;
+       TTY_LOCK_OWNED(tp);     /* debug assert */
 
-       funnel_state = thread_funnel_set(kernel_flock, TRUE);
        if ( (flag & FNONBLOCK) || ttywflush(tp))
                ttyflush(tp, FREAD | FWRITE);
-       thread_funnel_set(kernel_flock, funnel_state);
+
        return (0);
 }
 
+
 /*
+ * ttymodem (LDISC)
+ *
  * Handle modem control transition on a tty.
  * Flag indicates new state of carrier.
  * Returns 0 if the line should be turned off, otherwise 1.
+ *
+ * Locks:      Assumes tty_lock() is held prior to calling.
  */
 int
-ttymodem(tp, flag)
-       register struct tty *tp;
-       int flag;
+ttymodem(struct tty *tp, int flag)
 {
-       boolean_t funnel_state;
+       int rval = 1;           /* default return value */
 
-       funnel_state = thread_funnel_set(kernel_flock, TRUE);
+       TTY_LOCK_OWNED(tp);     /* debug assert */
 
        if (ISSET(tp->t_state, TS_CARR_ON) && ISSET(tp->t_cflag, MDMBUF)) {
                /*
@@ -1518,8 +1936,8 @@ ttymodem(tp, flag)
                        if (tp->t_session && tp->t_session->s_leader)
                                psignal(tp->t_session->s_leader, SIGHUP);
                        ttyflush(tp, FREAD | FWRITE);
-                       thread_funnel_set(kernel_flock, funnel_state);
-                       return (0);
+                       rval = 0;
+                       goto out;
                }
        } else {
                /*
@@ -1532,105 +1950,114 @@ ttymodem(tp, flag)
                ttwakeup(tp);
                ttwwakeup(tp);
        }
-       thread_funnel_set(kernel_flock, funnel_state);
-       return (1);
+
+out:
+       return (rval);
 }
 
+
 /*
  * Reinput pending characters after state switch
  * call at spltty().
+ *
+ * Locks:      Assumes tty_lock() is held for the call.
  */
 static void
-ttypend(tp)
-       register struct tty *tp;
+ttypend(struct tty *tp)
 {
        struct clist tq;
-       register int c;
+       int c;
+
+       TTY_LOCK_OWNED(tp);     /* debug assert */
 
        CLR(tp->t_lflag, PENDIN);
        SET(tp->t_state, TS_TYPEN);
-#ifndef NeXT
-       /*
-        * XXX this assumes too much about clist internals.  It may even
-        * fail if the cblock slush pool is empty.  We can't allocate more
-        * cblocks here because we are called from an interrupt handler
-        * and clist_alloc_cblocks() can wait.
-        */
-       tq = tp->t_rawq;
-       bzero(&tp->t_rawq, sizeof tp->t_rawq);
-       tp->t_rawq.c_cbmax = tq.c_cbmax;
-       tp->t_rawq.c_cbreserved = tq.c_cbreserved;
-#else
        tq = tp->t_rawq;
        tp->t_rawq.c_cc = 0;
-       tp->t_rawq.c_cf = tp->t_rawq.c_cl = 0;
-#endif /* !NeXT */
+       tp->t_rawq.c_cf = tp->t_rawq.c_cl = NULL;
        while ((c = getc(&tq)) >= 0)
                ttyinput(c, tp);
        CLR(tp->t_state, TS_TYPEN);
 }
 
+
 /*
+ * ttread (LDISC)
+ *
  * Process a read call on a tty device.
+ *
+ * Locks:      Assumes tty_lock() is held prior to calling.
  */
 int
-ttread(tp, uio, flag)
-       register struct tty *tp;
-       struct uio *uio;
-       int flag;
+ttread(struct tty *tp, struct uio *uio, int flag)
 {
-       register struct clist *qp;
-       register int c;
-       register tcflag_t lflag;
-       register cc_t *cc = tp->t_cc;
-       register struct proc *p = current_proc();
-       int s, first, error = 0;
+       struct clist *qp;
+       int c;
+       tcflag_t lflag;
+       cc_t *cc = tp->t_cc;
+       proc_t p = current_proc();
+       int first, error = 0;
        int has_etime = 0, last_cc = 0;
        long slp = 0;           /* XXX this should be renamed `timo'. */
-       boolean_t funnel_state;
        struct uthread *ut;
+       struct pgrp * pg;
 
-       funnel_state = thread_funnel_set(kernel_flock, TRUE);
+       TTY_LOCK_OWNED(tp);     /* debug assert */
 
-       ut = (struct uthread *)get_bsdthread_info(current_act());
+       ut = (struct uthread *)get_bsdthread_info(current_thread());
 
 loop:
-       s = spltty();
        lflag = tp->t_lflag;
        /*
         * take pending input first
         */
        if (ISSET(lflag, PENDIN)) {
                ttypend(tp);
-               splx(s);        /* reduce latency */
-               s = spltty();
                lflag = tp->t_lflag;    /* XXX ttypend() clobbers it */
        }
 
        /*
-        * Hang process if it's in the background.
+        * Signal the process if it's in the background.
         */
        if (isbackground(p, tp)) {
-               splx(s);
                if ((p->p_sigignore & sigmask(SIGTTIN)) ||
                   (ut->uu_sigmask & sigmask(SIGTTIN)) ||
-                   p->p_flag & P_PPWAIT || p->p_pgrp->pg_jobc == 0) {
-                       thread_funnel_set(kernel_flock, funnel_state);
-                       return (EIO);
+                   p->p_lflag & P_LPPWAIT) {
+                       error = EIO;
+                       goto err;
                }
-               pgsignal(p->p_pgrp, SIGTTIN, 1);
-               error = ttysleep(tp, &lbolt, TTIPRI | PCATCH | PTTYBLOCK, "ttybg2", 0);
-               if (error){
-                       thread_funnel_set(kernel_flock, funnel_state);
-                       return (error);
+               pg = proc_pgrp(p);
+               if (pg == PGRP_NULL) {
+                       error = EIO;
+                       goto err;
                }
-               goto loop;
+               if (pg->pg_jobc == 0) {
+                       /* SAFE: All callers drop the lock on return */
+                       tty_unlock(tp);
+                       pg_rele(pg);
+                       tty_lock(tp);
+                       error = EIO;
+                       goto err;
+               }
+               /* SAFE: All callers drop the lock on return */
+               tty_unlock(tp);
+               pgsignal(pg, SIGTTIN, 1);
+               pg_rele(pg);
+               tty_lock(tp);
+
+               /*
+                * We signalled ourself, so we need to act as if we
+                * have been "interrupted" from a "sleep" to act on
+                * the signal.  If it's a signal that stops the
+                * process, that's handled in the signal sending code.
+                */
+               error = EINTR;
+               goto err;
        }
 
        if (ISSET(tp->t_state, TS_ZOMBIE)) {
-               splx(s);
-               thread_funnel_set(kernel_flock, funnel_state);
-               return (0);     /* EOF */
+               /* EOF - returning 0 */
+               goto err;
        }
 
        /*
@@ -1644,20 +2071,17 @@ loop:
        if (flag & IO_NDELAY) {
                if (qp->c_cc > 0)
                        goto read;
-               if (!ISSET(lflag, ICANON) && cc[VMIN] == 0) {
-                       splx(s);
-                       thread_funnel_set(kernel_flock, funnel_state);
-                       return (0);
+               if (ISSET(lflag, ICANON) || cc[VMIN] != 0) {
+                       error = EWOULDBLOCK;
                }
-               splx(s);
-               thread_funnel_set(kernel_flock, funnel_state);
-               return (EWOULDBLOCK);
+               /* else polling - returning 0 */
+               goto err;
        }
        if (!ISSET(lflag, ICANON)) {
                int m = cc[VMIN];
                long t = cc[VTIME];
-               struct timeval etime, timecopy;
-               int x;
+               struct timeval timecopy;
+               struct timeval etime = {0, 0};  /* protected by !has_etime */
 
                /*
                 * Check each of the four combinations.
@@ -1674,9 +2098,7 @@ loop:
                                goto read;
 
                        /* m, t and qp->c_cc are all 0.  0 is enough input. */
-                       splx(s);
-                       thread_funnel_set(kernel_flock, funnel_state);
-                       return (0);
+                       goto err;
                }
                t *= 100000;            /* time in us */
 #define diff(t1, t2) (((t1).tv_sec - (t2).tv_sec) * 1000000 + \
@@ -1686,9 +2108,7 @@ loop:
                                goto sleep;
                        if (qp->c_cc >= m)
                                goto read;
-                       x = splclock();
-                       timecopy = time;
-                       splx(x);
+                       microuptime(&timecopy);
                        if (!has_etime) {
                                /* first character, start timer */
                                has_etime = 1;
@@ -1717,9 +2137,7 @@ loop:
                } else {        /* m == 0 */
                        if (qp->c_cc > 0)
                                goto read;
-                       x = splclock();
-                       timecopy = time;
-                       splx(x);
+                       microuptime(&timecopy);
                        if (!has_etime) {
                                has_etime = 1;
 
@@ -1731,9 +2149,7 @@ loop:
                        } else {
                                if (timercmp(&etime, &timecopy, <=)) {
                                        /* Timed out, but 0 is enough input. */
-                                       splx(s);
-                                       thread_funnel_set(kernel_flock, funnel_state);
-                                       return (0);
+                                       goto err;
                                }
                                slp = diff(etime, timecopy);
                        }
@@ -1748,7 +2164,7 @@ loop:
                 * is large (divide by `tick' and/or arrange to
                 * use hzto() if hz is large).
                 */
-               slp = (long) (((u_long)slp * hz) + 999999) / 1000000;
+               slp = (long) (((u_int32_t)slp * hz) + 999999) / 1000000;
                goto sleep;
        }
        if (qp->c_cc <= 0) {
@@ -1759,12 +2175,10 @@ sleep:
                error = ttysleep(tp, TSA_HUP_OR_INPUT(tp), TTIPRI | PCATCH,
                                 ISSET(tp->t_state, TS_CONNECTED) ?
                                 "ttyin" : "ttyhup", (int)slp);
-               splx(s);
                if (error == EWOULDBLOCK)
                        error = 0;
                else if (error) {
-                       thread_funnel_set(kernel_flock, funnel_state);
-                       return (error);
+                       goto err;
                }
                /*
                 * XXX what happens if another process eats some input
@@ -1776,24 +2190,19 @@ sleep:
                goto loop;
        }
 read:
-       splx(s);
        /*
         * Input present, check for input mapping and processing.
         */
        first = 1;
-#ifdef NeXT
        if (ISSET(lflag, ICANON)
        || (ISSET(lflag, IEXTEN | ISIG) == (IEXTEN | ISIG)) )
-#else
-       if (ISSET(lflag, ICANON | ISIG))
-#endif
                goto slowcase;
        for (;;) {
                char ibuf[IBUFSIZ];
                int icc;
 
-               icc = min(uio->uio_resid, IBUFSIZ);
-               icc = q_to_b(qp, ibuf, icc);
+               icc = MIN(uio_resid(uio), IBUFSIZ);
+               icc = q_to_b(qp, (u_char *)ibuf, icc);
                if (icc <= 0) {
                        if (first)
                                goto loop;
@@ -1804,14 +2213,9 @@ read:
                 * XXX if there was an error then we should ungetc() the
                 * unmoved chars and reduce icc here.
                 */
-#if NSNP > 0
-               if (ISSET(tp->t_lflag, ECHO) &&
-                   ISSET(tp->t_state, TS_SNOOP) && tp->t_sc != NULL)
-                       snpin((struct snoop *)tp->t_sc, ibuf, icc);
-#endif
                if (error)
                        break;
-               if (uio->uio_resid == 0)
+               if (uio_resid(uio) == 0)
                        break;
                first = 0;
        }
@@ -1829,10 +2233,17 @@ slowcase:
                 */
                if (CCEQ(cc[VDSUSP], c) &&
                    ISSET(lflag, IEXTEN | ISIG) == (IEXTEN | ISIG)) {
-                       pgsignal(tp->t_pgrp, SIGTSTP, 1);
+                       /*
+                        * SAFE: All callers drop the lock on return and
+                        * SAFE: current thread will not change out from
+                        * SAFE: under us in the "goto loop" case.
+                        */
+                       tty_unlock(tp);
+                       tty_pgsignal(tp, SIGTSTP, 1);
+                       tty_lock(tp);
                        if (first) {
-                               error = ttysleep(tp, &lbolt, TTIPRI | PCATCH,
-                                                "ttybg3", 0);
+                               error = ttysleep(tp, &ttread, TTIPRI | PCATCH,
+                                                "ttybg3", hz);
                                if (error)
                                        break;
                                goto loop;
@@ -1851,16 +2262,7 @@ slowcase:
                if (error)
                        /* XXX should ungetc(c, qp). */
                        break;
-#if NSNP > 0
-               /*
-                * Only snoop directly on input in echo mode.  Non-echoed
-                * input will be snooped later iff the application echoes it.
-                */
-               if (ISSET(tp->t_lflag, ECHO) &&
-                   ISSET(tp->t_state, TS_SNOOP) && tp->t_sc != NULL)
-                       snpinc((struct snoop *)tp->t_sc, (char)c);
-#endif
-               if (uio->uio_resid == 0)
+               if (uio_resid(uio) == 0)
                        break;
                /*
                 * In canonical mode check for a "break character"
@@ -1876,35 +2278,38 @@ out:
         * Look to unblock input now that (presumably)
         * the input queue has gone down.
         */
-       s = spltty();
        if (ISSET(tp->t_state, TS_TBLOCK) &&
            tp->t_rawq.c_cc + tp->t_canq.c_cc <= I_LOW_WATER)
                ttyunblock(tp);
-       splx(s);
 
-       thread_funnel_set(kernel_flock, funnel_state);
+err:
        return (error);
 }
 
+
 /*
  * Check the output queue on tp for space for a kernel message (from uprintf
  * or tprintf).  Allow some space over the normal hiwater mark so we don't
  * lose messages due to normal flow control, but don't let the tty run amok.
  * Sleeps here are not interruptible, but we return prematurely if new signals
  * arrive.
+ *
+ * Locks:      Assumes tty_lock() is held before calling
+ *
+ * Notes:      This function is called from tprintf() in subr_prf.c
  */
 int
-ttycheckoutq(tp, wait)
-       register struct tty *tp;
-       int wait;
+ttycheckoutq(struct tty *tp, int wait)
 {
-       int hiwat, s, oldsig;
+       int hiwat;
+       sigset_t oldsig;
        struct uthread *ut;
 
-       ut = (struct uthread *)get_bsdthread_info(current_act());
+       TTY_LOCK_OWNED(tp);     /* debug assert */
+
+       ut = (struct uthread *)get_bsdthread_info(current_thread());
 
        hiwat = tp->t_hiwat;
-       s = spltty();
        oldsig = wait ? ut->uu_siglist : 0;
        if (tp->t_outq.c_cc > hiwat + OBUFSIZ + 100)
                while (tp->t_outq.c_cc > hiwat) {
@@ -1912,89 +2317,102 @@ ttycheckoutq(tp, wait)
                        if (tp->t_outq.c_cc <= hiwat)
                                break;
                        if (wait == 0 || ut->uu_siglist != oldsig) {
-                               splx(s);
                                return (0);
                        }
                        SET(tp->t_state, TS_SO_OLOWAT);
-                       tsleep(TSA_OLOWAT(tp), PZERO - 1, "ttoutq", hz);
+                       ttysleep(tp, TSA_OLOWAT(tp), PZERO - 1, "ttoutq", hz);
                }
-       splx(s);
        return (1);
 }
 
+
 /*
+ * ttwrite (LDISC)
+ *
  * Process a write call on a tty device.
+ *
+ * Locks:      Assumes tty_lock() is held prior to calling.
  */
 int
-ttwrite(tp, uio, flag)
-       register struct tty *tp;
-       register struct uio *uio;
-       int flag;
+ttwrite(struct tty *tp, struct uio *uio, int flag)
 {
-       register char *cp = NULL;
-       register int cc, ce;
-       register struct proc *p;
-       int i, hiwat, cnt, error, s;
+       char *cp = NULL;
+       int cc, ce;
+       proc_t p;
+       int i, hiwat, error;
+       user_ssize_t count;
        char obuf[OBUFSIZ];
-       boolean_t funnel_state;
        struct uthread *ut;
+       struct pgrp * pg;
 
-       funnel_state = thread_funnel_set(kernel_flock, TRUE);
+       TTY_LOCK_OWNED(tp);     /* debug assert */
 
-       ut = (struct uthread *)get_bsdthread_info(current_act());
+       ut = (struct uthread *)get_bsdthread_info(current_thread());
        hiwat = tp->t_hiwat;
-       cnt = uio->uio_resid;
+       count = uio_resid(uio);
        error = 0;
        cc = 0;
 loop:
-       s = spltty();
        if (ISSET(tp->t_state, TS_ZOMBIE)) {
-               splx(s);
-               if (uio->uio_resid == cnt)
+               if (uio_resid(uio) == count)
                        error = EIO;
                goto out;
        }
        if (!ISSET(tp->t_state, TS_CONNECTED)) {
                if (flag & IO_NDELAY) {
-                       splx(s);
                        error = EWOULDBLOCK;
                        goto out;
                }
                error = ttysleep(tp, TSA_CARR_ON(tp), TTIPRI | PCATCH,
                                 "ttydcd", 0);
-               splx(s);
                if (error) {
                        goto out; }
                goto loop;
        }
-       splx(s);
        /*
-        * Hang the process if it's in the background.
+        * Signal the process if it's in the background.
         */
        p = current_proc();
        if (isbackground(p, tp) &&
-           ISSET(tp->t_lflag, TOSTOP) && (p->p_flag & P_PPWAIT) == 0 &&
+           ISSET(tp->t_lflag, TOSTOP) && (p->p_lflag & P_LPPWAIT) == 0 &&
            (p->p_sigignore & sigmask(SIGTTOU)) == 0 &&
            (ut->uu_sigmask & sigmask(SIGTTOU)) == 0) {
-               if (p->p_pgrp->pg_jobc == 0) {
+
+               pg = proc_pgrp(p);
+               if (pg == PGRP_NULL) {
                        error = EIO;
                        goto out;
                }
-               pgsignal(p->p_pgrp, SIGTTOU, 1);
-               error = ttysleep(tp, &lbolt, TTIPRI | PCATCH | PTTYBLOCK, "ttybg4", 0);
-               if (error)
+               if (pg->pg_jobc == 0) {
+                       /* SAFE: All callers drop the lock on return */
+                       tty_unlock(tp);
+                       pg_rele(pg);
+                       tty_lock(tp);
+                       error = EIO;
+                       goto out;
+               }
+               /* SAFE: All callers drop the lock on return */
+               tty_unlock(tp);
+               pgsignal(pg, SIGTTOU, 1);
+               pg_rele(pg);
+               tty_lock(tp);
+               /*
+                * We signalled ourself, so we need to act as if we
+                * have been "interrupted" from a "sleep" to act on
+                * the signal.  If it's a signal that stops the
+                * process, that's handled in the signal sending code.
+                */
+               error = EINTR;
                        goto out;
-               goto loop;
        }
        /*
         * Process the user's data in at most OBUFSIZ chunks.  Perform any
         * output translation.  Keep track of high water mark, sleep on
         * overflow awaiting device aid in acquiring new space.
         */
-       while (uio->uio_resid > 0 || cc > 0) {
+       while (uio_resid(uio) > 0 || cc > 0) {
                if (ISSET(tp->t_lflag, FLUSHO)) {
-                       uio->uio_resid = 0;
-                       thread_funnel_set(kernel_flock, funnel_state);
+                       uio_setresid(uio, 0);
                        return (0);
                }
                if (tp->t_outq.c_cc > hiwat)
@@ -2004,17 +2422,13 @@ loop:
                 * leftover from last time.
                 */
                if (cc == 0) {
-                       cc = min(uio->uio_resid, OBUFSIZ);
+                       cc = MIN(uio_resid(uio), OBUFSIZ);
                        cp = obuf;
                        error = uiomove(cp, cc, uio);
                        if (error) {
                                cc = 0;
                                break;
                        }
-#if NSNP > 0
-                       if (ISSET(tp->t_state, TS_SNOOP) && tp->t_sc != NULL)
-                               snpin((struct snoop *)tp->t_sc, cp, cc);
-#endif
                }
                /*
                 * If nothing fancy need be done, grab those characters we
@@ -2030,7 +2444,7 @@ loop:
                                ce = cc;
                        else {
                                ce = cc - scanc((u_int)cc, (u_char *)cp,
-                                               (u_char *)char_type, CCLASSMASK);
+                                               char_type, CCLASSMASK);
                                /*
                                 * If ce is zero, then we're processing
                                 * a special character through ttyoutput.
@@ -2038,23 +2452,8 @@ loop:
                                if (ce == 0) {
                                        tp->t_rocount = 0;
                                        if (ttyoutput(*cp, tp) >= 0) {
-#ifdef NeXT
                                                /* out of space */
                                                goto overfull;
-#else
-                                               /* No Clists, wait a bit. */
-                                               ttstart(tp);
-                                               if (flag & IO_NDELAY) {
-                                                       error = EWOULDBLOCK;
-                                                       goto out;
-                                               }
-                                               error = ttysleep(tp, &lbolt,
-                                                                TTOPRI|PCATCH,
-                                                                "ttybf1", 0);
-                                               if (error)
-                                                       goto out;
-                                               goto loop;
-#endif /* NeXT */
                                        }
                                        cp++;
                                        cc--;
@@ -2073,28 +2472,16 @@ loop:
                         * requiring special handling by ttyoutput.
                         */
                        tp->t_rocount = 0;
-                       i = b_to_q(cp, ce, &tp->t_outq);
+                       i = b_to_q((u_char *)cp, ce, &tp->t_outq);
                        ce -= i;
                        tp->t_column += ce;
-                       cp += ce, cc -= ce, tk_nout += ce;
+                       cp += ce;
+                       cc -= ce;
+                       tk_nout += ce;
                        tp->t_outcc += ce;
                        if (i > 0) {
-#ifdef NeXT
                                /* out of space */
                                goto overfull;
-#else
-                               /* No Clists, wait a bit. */
-                               ttstart(tp);
-                               if (flag & IO_NDELAY) {
-                                       error = EWOULDBLOCK;
-                                       goto out;
-                               }
-                               error = ttysleep(tp, &lbolt, TTOPRI | PCATCH,
-                                                "ttybf2", 0);
-                               if (error)
-                                       goto out;
-                               goto loop;
-#endif /* NeXT */
                        }
                        if (ISSET(tp->t_lflag, FLUSHO) ||
                            tp->t_outq.c_cc > hiwat)
@@ -2108,11 +2495,9 @@ out:
         * offset and iov pointers have moved forward, but it doesn't matter
         * (the call will either return short or restart with a new uio).
         */
-       uio->uio_resid += cc;
-       thread_funnel_set(kernel_flock, funnel_state);
+       uio_setresid(uio, (uio_resid(uio) + cc));
        return (error);
 
-#ifdef NeXT
 overfull:
 
        /*
@@ -2122,29 +2507,23 @@ overfull:
         * proceed as normal.
         */
        hiwat = tp->t_outq.c_cc - 1;
-#endif
 
 ovhiwat:
        ttstart(tp);
-       s = spltty();
        /*
         * This can only occur if FLUSHO is set in t_lflag,
         * or if ttstart/oproc is synchronous (or very fast).
         */
        if (tp->t_outq.c_cc <= hiwat) {
-               splx(s);
                goto loop;
        }
        if (flag & IO_NDELAY) {
-               splx(s);
-               uio->uio_resid += cc;
-               thread_funnel_set(kernel_flock, funnel_state);
-               return (uio->uio_resid == cnt ? EWOULDBLOCK : 0);
+               uio_setresid(uio, (uio_resid(uio) + cc));
+               return (uio_resid(uio) == count ? EWOULDBLOCK : 0);
        }
        SET(tp->t_state, TS_SO_OLOWAT);
        error = ttysleep(tp, TSA_OLOWAT(tp), TTOPRI | PCATCH, "ttywri",
                         tp->t_timeout);
-       splx(s);
        if (error == EWOULDBLOCK)
                error = EIO;
        if (error)
@@ -2152,18 +2531,21 @@ ovhiwat:
        goto loop;
 }
 
+
 /*
  * Rubout one character from the rawq of tp
  * as cleanly as possible.
+ *
+ * Locks:      Assumes tty_lock() is held prior to calling.
  */
 static void
-ttyrub(c, tp)
-       register int c;
-       register struct tty *tp;
+ttyrub(int c, struct tty *tp)
 {
-       register u_char *cp;
-       register int savecol;
-       int tabc, s;
+       u_char *cp;
+       int savecol;
+       int tabc;
+
+       TTY_LOCK_OWNED(tp);     /* debug assert */
 
        if (!ISSET(tp->t_lflag, ECHO) || ISSET(tp->t_lflag, EXTPROC))
                return;
@@ -2182,7 +2564,9 @@ ttyrub(c, tp)
                        CLR(c, ~TTY_CHARMASK);
                        switch (CCLASS(c)) {
                        case ORDINARY:
-                               ttyrubo(tp, 1);
+                               if(!(ISSET(tp->t_iflag, IUTF8) && CCONT(c))) {
+                                       ttyrubo(tp, 1);
+                               }
                                break;
                        case BACKSPACE:
                        case CONTROL:
@@ -2197,25 +2581,15 @@ ttyrub(c, tp)
                                        ttyretype(tp);
                                        return;
                                }
-                               s = spltty();
                                savecol = tp->t_column;
                                SET(tp->t_state, TS_CNTTB);
                                SET(tp->t_lflag, FLUSHO);
                                tp->t_column = tp->t_rocol;
-#ifndef NeXT
-                               cp = tp->t_rawq.c_cf;
-                               if (cp)
-                                       tabc = *cp;     /* XXX FIX NEXTC */
-                               for (; cp; cp = nextc(&tp->t_rawq, cp, &tabc))
-                                       ttyecho(tabc, tp);
-#else
                                for (cp = firstc(&tp->t_rawq, &tabc); cp;
                                    cp = nextc(&tp->t_rawq, cp, &tabc))
                                        ttyecho(tabc, tp);
-#endif /* !NeXT */
                                CLR(tp->t_lflag, FLUSHO);
                                CLR(tp->t_state, TS_CNTTB);
-                               splx(s);
 
                                /* savecol will now be length of the tab. */
                                savecol -= tp->t_column;
@@ -2227,7 +2601,7 @@ ttyrub(c, tp)
                                break;
                        default:                        /* XXX */
 #define        PANICSTR        "ttyrub: would panic c = %d, val = %d\n"
-                               (void)printf(PANICSTR, c, CCLASS(c));
+                               printf(PANICSTR, c, CCLASS(c));
 #ifdef notdef
                                panic(PANICSTR, c, CCLASS(c));
 #endif
@@ -2244,33 +2618,39 @@ ttyrub(c, tp)
        --tp->t_rocount;
 }
 
+
 /*
- * Back over cnt characters, erasing them.
+ * Back over count characters, erasing them.
+ *
+ * Locks:      Assumes tty_lock() is held prior to calling.
  */
 static void
-ttyrubo(tp, cnt)
-       register struct tty *tp;
-       int cnt;
+ttyrubo(struct tty *tp, int count)
 {
+       TTY_LOCK_OWNED(tp);     /* debug assert */
 
-       while (cnt-- > 0) {
+       while (count-- > 0) {
                (void)ttyoutput('\b', tp);
                (void)ttyoutput(' ', tp);
                (void)ttyoutput('\b', tp);
        }
 }
 
+
 /*
  * ttyretype --
  *     Reprint the rawq line.  Note, it is assumed that c_cc has already
  *     been checked.
+ *
+ * Locks:      Assumes tty_lock() is held prior to calling.
  */
 static void
-ttyretype(tp)
-       register struct tty *tp;
+ttyretype(struct tty *tp)
 {
-       register u_char *cp;
-       int s, c;
+       u_char *cp;
+       int c;
+
+       TTY_LOCK_OWNED(tp);     /* debug assert */
 
        /* Echo the reprint character. */
        if (tp->t_cc[VREPRINT] != _POSIX_VDISABLE)
@@ -2283,35 +2663,26 @@ ttyretype(tp)
         * FIX: NEXTC IS BROKEN - DOESN'T CHECK QUOTE
         * BIT OF FIRST CHAR.
         */
-       s = spltty();
-#ifndef NeXT
-       for (cp = tp->t_canq.c_cf, c = (cp != NULL ? *cp : 0);
-           cp != NULL; cp = nextc(&tp->t_canq, cp, &c))
-               ttyecho(c, tp);
-       for (cp = tp->t_rawq.c_cf, c = (cp != NULL ? *cp : 0);
-           cp != NULL; cp = nextc(&tp->t_rawq, cp, &c))
-               ttyecho(c, tp);
-#else NeXT
        for (cp = firstc(&tp->t_canq, &c); cp; cp = nextc(&tp->t_canq, cp, &c))
                ttyecho(c, tp);
        for (cp = firstc(&tp->t_rawq, &c); cp; cp = nextc(&tp->t_rawq, cp, &c))
                ttyecho(c, tp);
-#endif /* !NeXT */
        CLR(tp->t_state, TS_ERASE);
-       splx(s);
 
        tp->t_rocount = tp->t_rawq.c_cc;
        tp->t_rocol = 0;
 }
 
+
 /*
  * Echo a typed character to the terminal.
+ *
+ * Locks:      Assumes tty_lock() is held prior to calling.
  */
 static void
-ttyecho(c, tp)
-       register int c;
-       register struct tty *tp;
+ttyecho(int c, struct tty *tp)
 {
+       TTY_LOCK_OWNED(tp);     /* debug assert */
 
        if (!ISSET(tp->t_state, TS_CNTTB))
                CLR(tp->t_lflag, FLUSHO);
@@ -2332,36 +2703,52 @@ ttyecho(c, tp)
        (void)ttyoutput(c, tp);
 }
 
+
 /*
  * Wake up any readers on a tty.
+ *
+ * Locks:      Assumes tty_lock() is held for the call.
  */
 void
-ttwakeup(tp)
-       register struct tty *tp;
+ttwakeup(struct tty *tp)
 {
+       TTY_LOCK_OWNED(tp);     /* debug assert */
 
-#ifndef NeXT
-       if (tp->t_rsel.si_pid != 0)
-#endif
-               selwakeup(&tp->t_rsel);
-       if (ISSET(tp->t_state, TS_ASYNC))
-               pgsignal(tp->t_pgrp, SIGIO, 1);
+       selwakeup(&tp->t_rsel);
+       KNOTE(&tp->t_rsel.si_note, 1);
+       if (ISSET(tp->t_state, TS_ASYNC)) {
+               /*
+                * XXX: Callers may not revalidate it the tty is closed
+                * XXX: out from under them by another thread, but we do
+                * XXX: not support queued signals.  This should be safe,
+                * XXX: since the process we intend to wakeup is in the
+                * XXX: process group, and will wake up because of the
+                * XXX: signal anyway.
+                */
+               tty_unlock(tp);
+               tty_pgsignal(tp, SIGIO, 1);
+               tty_lock(tp);
+       }
        wakeup(TSA_HUP_OR_INPUT(tp));
 }
 
+
 /*
+ * ttwwakeup (LDISC)
+ *
  * Wake up any writers on a tty.
+ *
+ * Locks:      Assumes tty_lock() is held prior to calling.
  */
 void
-ttwwakeup(tp)
-       register struct tty *tp;
+ttwwakeup(struct tty *tp)
 {
-#ifndef NeXT
-       if (tp->t_wsel.si_pid != 0 && tp->t_outq.c_cc <= tp->t_lowat)
-#else
-       if (tp->t_outq.c_cc <= tp->t_lowat)
-#endif
+       TTY_LOCK_OWNED(tp);     /* debug assert */
+
+       if (tp->t_outq.c_cc <= tp->t_lowat) {
                selwakeup(&tp->t_wsel);
+               KNOTE(&tp->t_wsel.si_note, 1);
+       }
        if (ISSET(tp->t_state, TS_BUSY | TS_SO_OCOMPLETE) ==
            TS_SO_OCOMPLETE && tp->t_outq.c_cc == 0) {
                CLR(tp->t_state, TS_SO_OCOMPLETE);
@@ -2374,34 +2761,39 @@ ttwwakeup(tp)
        }
 }
 
+
 /*
  * Look up a code for a specified speed in a conversion table;
  * used by drivers to map software speed values to hardware parameters.
+ *
+ * Notes:      No locks are assumed for this function; it does not
+ *             directly access struct tty.
  */
 int
-ttspeedtab(speed, table)
-       int speed;
-       register struct speedtab *table;
+ttspeedtab(int speed, struct speedtab *table)
 {
-
        for ( ; table->sp_speed != -1; table++)
                if (table->sp_speed == speed)
                        return (table->sp_code);
        return (-1);
 }
 
+
 /*
  * Set tty hi and low water marks.
  *
  * Try to arrange the dynamics so there's about one second
  * from hi to low water.
  *
+ * Locks:      Assumes tty_lock() is held prior to calling.
  */
 void
-ttsetwater(tp)
-       struct tty *tp;
+ttsetwater(struct tty *tp)
 {
-       register int cps, x;
+       int cps;
+       unsigned int x;
+
+       TTY_LOCK_OWNED(tp);     /* debug assert */
 
 #define CLAMP(x, h, l) ((x) > h ? h : ((x) < l) ? l : (x))
 
@@ -2413,20 +2805,137 @@ ttsetwater(tp)
 #undef CLAMP
 }
 
-/* NeXT ttyinfo has been converted to the MACH kernel */
+/* ttyinfo has been converted to the MACH kernel */
 #include <mach/thread_info.h>
 
+/* XXX Should be in Mach header <kern/thread.h>, but doesn't work */
+extern kern_return_t   thread_info_internal(thread_t thread,
+                               thread_flavor_t flavor,
+                               thread_info_t thread_info_out,
+                               mach_msg_type_number_t *thread_info_count);
+
+
 /*
  * Report on state of foreground process group.
+ *
+ * Locks:      Assumes tty_lock() is held prior to calling.
  */
 void
-ttyinfo(tp)
-       register struct tty *tp;
+ttyinfo_locked(struct tty *tp)
 {
-  /* NOT IMPLEMENTED FOR MACH */
+       int             load;
+       thread_t        thread;
+       uthread_t       uthread;
+       proc_t          p;
+       proc_t          pick;
+       pid_t pickpid;
+       const char      *state;
+       struct timeval  utime;
+       struct timeval  stime;
+       thread_basic_info_data_t        basic_info;
+       mach_msg_type_number_t          mmtn = THREAD_BASIC_INFO_COUNT;
+       struct pgrp * pg;
+
+       TTY_LOCK_OWNED(tp);     /* debug assert */
+
+       if (ttycheckoutq(tp,0) == 0)
+               return;
+
+       /* Print load average. */
+       load = (averunnable.ldavg[0] * 100 + FSCALE / 2) >> FSHIFT;
+       ttyprintf(tp, "load: %d.%02d ", load / 100, load % 100);
+
+       /*
+        * On return following a ttyprintf(), we set tp->t_rocount to 0 so
+        * that pending input will be retyped on BS.
+        */
+       if (tp->t_session == NULL) {
+               ttyprintf(tp, "not a controlling terminal\n");
+               tp->t_rocount = 0;
+               return;
 }
+       if (tp->t_pgrp == NULL) {
+               ttyprintf(tp, "no foreground process group\n");
+               tp->t_rocount = 0;
+               return;
+       }
+       /* first process in process group */
+       /* XXX is there a need for pgrp lock ? */
+       if ((p = tp->t_pgrp->pg_members.lh_first) == NULL) {
+               ttyprintf(tp, "empty foreground process group\n");
+               tp->t_rocount = 0;
+               return;
+       }
+
+       /*
+        * Pick the most interesting process and copy some of its
+        * state for printing later.
+        */
+       pg = proc_pgrp(p);
+       pgrp_lock(pg);
+       /* the proc_compare is non blocking fn, no need to use iterator */
+       for (pick = NULL; p != NULL; p = p->p_pglist.le_next) {
+               if (proc_compare(pick, p)) {
+                       pick = p;
+                       pickpid = p->p_pid;
+               } else {
+                       pickpid = pick->p_pid;
+               }
+       }
+       pgrp_unlock(pg);
+       /* SAFE: All callers drop the lock on return */
+       tty_unlock(tp);
+       pg_rele(pg);
+       tty_lock(tp);
+
+       pick = proc_find(pickpid);
+       if (pick == PROC_NULL)
+               return;
+
+       if (TAILQ_EMPTY(&pick->p_uthlist) ||
+           (uthread = TAILQ_FIRST(&pick->p_uthlist)) == NULL ||
+           (thread = vfs_context_thread(&uthread->uu_context)) == NULL ||
+           (thread_info_internal(thread, THREAD_BASIC_INFO, (thread_info_t)&basic_info, &mmtn) != KERN_SUCCESS)) {
+               ttyprintf(tp, "foreground process without thread\n");
+               tp->t_rocount = 0;
+               proc_rele(pick);
+               return;
+       }
+
+       switch(basic_info.run_state) {
+       case TH_STATE_RUNNING:
+               state = "running";
+               break;
+       case TH_STATE_STOPPED:
+               state = "stopped";
+               break;
+       case TH_STATE_WAITING:
+               state = "waiting";
+               break;
+       case TH_STATE_UNINTERRUPTIBLE:
+               state = "uninterruptible";
+               break;
+       case TH_STATE_HALTED:
+               state = "halted";
+               break;
+       default:
+               state = "unknown";
+               break;
+       }
+       calcru(pick, &utime, &stime, NULL);
+       proc_rele(pick);
+
+       /* Print command, pid, state, utime, and stime */
+       ttyprintf(tp, " cmd: %s %d %s %ld.%02du %ld.%02ds\n",
+               pick->p_comm,
+               pick->p_pid,
+               state,
+               (long)utime.tv_sec, utime.tv_usec / 10000,
+               (long)stime.tv_sec, stime.tv_usec / 10000);
+       tp->t_rocount = 0;
+}
+
 
-#ifndef NeXT
 /*
  * Returns 1 if p2 is "better" than p1
  *
@@ -2436,8 +2945,7 @@ ttyinfo(tp)
  *     2) Runnable processes are favored over anything else.  The runner
  *        with the highest cpu utilization is picked (p_estcpu).  Ties are
  *        broken by picking the highest pid.
- *     3) The sleeper with the shortest sleep time is next.  With ties,
- *        we pick out just "short-term" sleepers (P_SINTR == 0).
+ *     3) The sleeper with the shortest sleep time is next.
  *     4) Further ties are broken by picking the highest pid.
  */
 #define ISRUN(p)       (((p)->p_stat == SRUN) || ((p)->p_stat == SIDL))
@@ -2446,10 +2954,15 @@ ttyinfo(tp)
 #define ONLYB   1
 #define BOTH    3
 
+/*
+ * Locks:      pgrp_lock(p2) held on call to this function
+ *             tty_lock(tp) for p2's tty, for which p2 is the foreground
+ *                     process, held on call to this function
+ */
 static int
-proc_compare(p1, p2)
-       register struct proc *p1, *p2;
+proc_compare(proc_t p1, proc_t p2)
 {
+       /* NOTE THIS FN needs to be NON BLOCKING */
 
        if (p1 == NULL)
                return (1);
@@ -2465,10 +2978,13 @@ proc_compare(p1, p2)
                /*
                 * tie - favor one with highest recent cpu utilization
                 */
+#ifdef _PROC_HAS_SCHEDINFO_
+               /* Without the support the fields are always zero */
                if (p2->p_estcpu > p1->p_estcpu)
                        return (1);
                if (p1->p_estcpu > p2->p_estcpu)
                        return (0);
+#endif /* _PROC_HAS_SCHEDINFO_ */
                return (p2->p_pid > p1->p_pid); /* tie - return highest pid */
        }
        /*
@@ -2485,131 +3001,585 @@ proc_compare(p1, p2)
        /*
         * pick the one with the smallest sleep time
         */
+#ifdef _PROC_HAS_SCHEDINFO_
+       /* Without the support the fields are always zero */
        if (p2->p_slptime > p1->p_slptime)
                return (0);
        if (p1->p_slptime > p2->p_slptime)
                return (1);
-       /*
-        * favor one sleeping in a non-interruptible sleep
-        */
-       if (p1->p_flag & P_SINTR && (p2->p_flag & P_SINTR) == 0)
-               return (1);
-       if (p2->p_flag & P_SINTR && (p1->p_flag & P_SINTR) == 0)
-               return (0);
+#endif /* _PROC_HAS_SCHEDINFO_ */
        return (p2->p_pid > p1->p_pid);         /* tie - return highest pid */
 }
-#endif /* NeXT */
+
 
 /*
  * Output char to tty; console putchar style.
+ *
+ * Locks:      Assumes tty_lock() is held prior to calling.
+ *
+ * Notes:      Only ever called from putchar() in subr_prf.c
  */
 int
-tputchar(c, tp)
-       int c;
-       struct tty *tp;
+tputchar(int c, struct tty *tp)
 {
-       register int s;
+       TTY_LOCK_OWNED(tp);     /* debug assert */
 
-       s = spltty();
        if (!ISSET(tp->t_state, TS_CONNECTED)) {
-               splx(s);
                return (-1);
        }
        if (c == '\n')
                (void)ttyoutput('\r', tp);
        (void)ttyoutput(c, tp);
        ttstart(tp);
-       splx(s);
        return (0);
 }
 
+
 /*
+ * ttysleep
+ *
+ * Sleep on a wait channel waiting for an interrupt or a condition to come
+ * true so that we are woken up.
+ *
+ * Parameters: tp                      Tty going to sleep
+ *             chan                    The sleep channel (usually an address
+ *                                     of a structure member)
+ *             pri                     priority and flags
+ *             wmesg                   Wait message; shows up in debugger,
+ *                                     should show up in "ps", but doesn't
+ *             timo                    Timeout for the sleep
+ *
+ * Returns:    0                       Condition came true
+ *             ERESTART                Upper layer must redrive the call;
+ *                                     this is usually done by the Libc
+ *                                     stub in user space
+ *     msleep0:EINTR                   Interrupted (usually a signal)
+ *     msleep0:ERESTART                Interrupted (usually a masked signal)
+ *     msleep0:EWOULDBLOCK             Timeout (timo) already expired
+ *
+ * Locks:      Assumes tty_lock() is held prior to calling.
+ *
  * Sleep on chan, returning ERESTART if tty changed while we napped and
- * returning any errors (e.g. EINTR/EWOULDBLOCK) reported by tsleep.  If
+ * returning any errors (e.g. EINTR/EWOULDBLOCK) reported by msleep0.  If
  * the tty is revoked, restarting a pending call will redo validation done
  * at the start of the call.
  */
 int
-ttysleep(tp, chan, pri, wmesg, timo)
-       struct tty *tp;
-       void *chan;
-       int pri, timo;
-       char *wmesg;
+ttysleep(struct tty *tp, void *chan, int pri, const char *wmesg, int timo)
 {
        int error;
        int gen;
 
+       TTY_LOCK_OWNED(tp);
+
        gen = tp->t_gen;
-       error = tsleep(chan, pri, wmesg, timo);
+       /* Use of msleep0() avoids conversion timo/timespec/timo */
+       error = msleep0(chan, &tp->t_lock, pri, wmesg, timo, (int (*)(int))0);
        if (error)
                return (error);
        return (tp->t_gen == gen ? 0 : ERESTART);
 }
 
-#ifdef NeXT
+
 /*
  * Allocate a tty structure and its associated buffers.
+ *
+ * Parameters: void
+ *
+ * Returns:    !NULL                           Address of new struct tty
+ *             NULL                            Error ("ENOMEM")
+ *
+ * Locks:      The tty_lock() of the returned tty is not held when it
+ *             is returned.
  */
 struct tty *
-ttymalloc()
+ttymalloc(void)
 {
        struct tty *tp;
 
-       MALLOC(tp, struct tty *, sizeof(struct tty), M_TTYS, M_WAITOK);
-       bzero(tp, sizeof *tp);
-       /* XXX: default to TTYCLSIZE(1024) chars for now */
-       clalloc(&tp->t_rawq, TTYCLSIZE, 1);
-       clalloc(&tp->t_canq, TTYCLSIZE, 1);
-       /* output queue doesn't need quoting */
-       clalloc(&tp->t_outq, TTYCLSIZE, 0);
-       return(tp);
+       MALLOC(tp, struct tty *, sizeof(struct tty), M_TTYS, M_WAITOK|M_ZERO);
+       if (tp != NULL) {
+               /* XXX: default to TTYCLSIZE(1024) chars for now */
+               clalloc(&tp->t_rawq, TTYCLSIZE, 1);
+               clalloc(&tp->t_canq, TTYCLSIZE, 1);
+               /* output queue doesn't need quoting */
+               clalloc(&tp->t_outq, TTYCLSIZE, 0);
+               lck_mtx_init(&tp->t_lock, tty_lck_grp, tty_lck_attr);
+               klist_init(&tp->t_rsel.si_note);
+               klist_init(&tp->t_wsel.si_note);
+               tp->t_refcnt = 1;
+       }
+       return (tp);
 }
 
 /*
- * Free a tty structure and its buffers.
+ * Increment the reference count on a tty.
  */
 void
-ttyfree(tp)
-struct tty *tp;
+ttyhold(struct tty *tp)
 {
+       TTY_LOCK_OWNED(tp);
+       tp->t_refcnt++;
+}
+
+/*
+ * Drops a reference count on a tty structure; if the reference count reaches
+ * zero, then also frees the structure and associated buffers.
+ */
+void
+ttyfree(struct tty *tp)
+{
+       TTY_LOCK_NOTOWNED(tp);
+
+       tty_lock(tp);
+       if (--tp->t_refcnt == 0) {
+               tty_unlock(tp);
+               ttydeallocate(tp);
+       } else if (tp->t_refcnt < 0) {
+               panic("%s: freeing free tty %p", __func__, tp);
+       } else
+               tty_unlock(tp);
+}
+
+/*
+ * Deallocate a tty structure and its buffers.
+ *
+ * Locks:      The tty_lock() is assumed to not be held at the time of
+ *             the free; this function destroys the mutex.
+ */
+static void
+ttydeallocate(struct tty *tp)
+{
+       TTY_LOCK_NOTOWNED(tp);  /* debug assert */
+
+#if DEBUG
+       if (!(SLIST_EMPTY(&tp->t_rsel.si_note) && SLIST_EMPTY(&tp->t_wsel.si_note))) {
+               panic("knotes hooked into a tty when the tty is freed.\n");
+       }
+#endif /* DEBUG */
+
        clfree(&tp->t_rawq);
        clfree(&tp->t_canq);
        clfree(&tp->t_outq);
+       lck_mtx_destroy(&tp->t_lock, tty_lck_grp);
        FREE(tp, M_TTYS);
 }
 
-#else /* !NeXT */
 
-#ifdef notyet
 /*
- * XXX this is usable not useful or used.  Most tty drivers have
- * ifdefs for using ttymalloc() but assume a different interface.
+ * Locks:      Assumes tty_lock() is held prior to calling.
  */
+int
+isbackground(proc_t p, struct tty  *tp)
+{
+       TTY_LOCK_OWNED(tp);
+
+       return (tp->t_session != NULL && p->p_pgrp != NULL && (p->p_pgrp != tp->t_pgrp) && isctty_sp(p, tp, p->p_pgrp->pg_session));
+}
+
+static int 
+isctty(proc_t p, struct tty  *tp)
+{
+       int retval;
+       struct session * sessp;
+
+       sessp = proc_session(p);
+       retval = (sessp == tp->t_session && p->p_flag & P_CONTROLT);
+       session_rele(sessp);
+       return(retval);
+}
+
+static int
+isctty_sp(proc_t p, struct tty  *tp, struct session *sessp)
+{
+       return(sessp == tp->t_session && p->p_flag & P_CONTROLT);
+
+}
+
+
+static int  filt_ttyattach(struct knote *kn, struct kevent_internal_s *kev);
+static void filt_ttydetach(struct knote *kn);
+static int  filt_ttyevent(struct knote *kn, long hint);
+static int  filt_ttytouch(struct knote *kn, struct kevent_internal_s *kev);
+static int  filt_ttyprocess(struct knote *kn, struct filt_process_s *data, struct kevent_internal_s *kev);
+
+SECURITY_READ_ONLY_EARLY(struct filterops) tty_filtops = {
+       .f_isfd    = 1,
+       .f_attach  = filt_ttyattach,
+       .f_detach  = filt_ttydetach,
+       .f_event   = filt_ttyevent,
+       .f_touch   = filt_ttytouch,
+       .f_process = filt_ttyprocess
+};
+
 /*
- * Allocate a tty struct.  Clists in the struct will be allocated by
- * ttyopen().
+ * Called with struct tty locked. Returns non-zero if there is data to be read
+ * or written.
  */
-struct tty *
-ttymalloc()
+static int
+filt_tty_common(struct knote *kn, struct tty *tp)
+{
+       int retval = 0;
+
+       TTY_LOCK_OWNED(tp); /* debug assert */
+
+       if (tp->t_state & TS_ZOMBIE) {
+               kn->kn_flags |= EV_EOF;
+               return 1;
+       }
+
+       switch (knote_get_seltype(kn)) {
+       case FREAD:
+               retval = ttnread(tp);
+               break;
+       case FWRITE:
+               if ((tp->t_outq.c_cc <= tp->t_lowat) &&
+                   (tp->t_state & TS_CONNECTED)) {
+                       retval = tp->t_hiwat - tp->t_outq.c_cc;
+               }
+               break;
+       }
+
+       kn->kn_data = retval;
+
+       /*
+        * TODO(mwidmann, jandrus): For native knote low watermark support,
+        * check the kn_sfflags for NOTE_LOWAT and check against kn_sdata.
+        *
+        * res = ((kn->kn_sfflags & NOTE_LOWAT) != 0) ?
+        *        (kn->kn_data >= kn->kn_sdata) : kn->kn_data;
+        */
+
+       return retval;
+}
+
+/*
+ * Find the struct tty from a waitq, which is a member of one of the two struct
+ * selinfos inside the struct tty.  Use the seltype to determine which selinfo.
+ */
+static struct tty *
+tty_from_waitq(struct waitq *wq, int seltype)
 {
-        struct tty *tp;
+       struct selinfo *si;
+       struct tty *tp = NULL;
+
+       /*
+        * The waitq is part of the selinfo structure managed by the driver. For
+        * certain drivers, we want to hook the knote into the selinfo
+        * structure's si_note field so selwakeup can call KNOTE.
+        *
+        * While 'wq' is not really a queue element, this macro only uses the
+        * pointer to calculate the offset into a structure given an element
+        * name.
+        */
+       si = qe_element(wq, struct selinfo, si_waitq);
+
+       /*
+        * For TTY drivers, the selinfo structure is somewhere in the struct
+        * tty. There are two different selinfo structures, and the one used
+        * corresponds to the type of filter requested.
+        *
+        * While 'si' is not really a queue element, this macro only uses the
+        * pointer to calculate the offset into a structure given an element
+        * name.
+        */
+       switch (seltype) {
+       case FREAD:
+               tp = qe_element(si, struct tty, t_rsel);
+               break;
+       case FWRITE:
+               tp = qe_element(si, struct tty, t_wsel);
+               break;
+       }
 
-        tp = _MALLOC(sizeof *tp, M_TTYS, M_WAITOK);
-        bzero(tp, sizeof *tp);
-        return (tp);
+       return tp;
+}
+
+static struct tty *
+tty_from_knote(struct knote *kn)
+{
+       return (struct tty *)kn->kn_hook;
 }
-#endif
 
-#if 0 /* XXX not yet usable: session leader holds a ref (see kern_exit.c). */
 /*
- * Free a tty struct.  Clists in the struct should have been freed by
- * ttyclose().
+ * Try to lock the TTY structure associated with a knote.
+ *
+ * On success, this function returns a locked TTY structure.  Otherwise, NULL is
+ * returned.
  */
-void
-ttyfree(tp)
+__attribute__((warn_unused_result))
+static struct tty *
+tty_lock_from_knote(struct knote *kn)
+{
+       struct tty *tp = tty_from_knote(kn);
+       if (tp) {
+               tty_lock(tp);
+       }
+
+       return tp;
+}
+
+/*
+ * Set the knote's struct tty to the kn_hook field.
+ *
+ * The idea is to fake a call to select with our own waitq set.  If the driver
+ * calls selrecord, we'll get a link to their waitq and access to the tty
+ * structure.
+ *
+ * Returns -1 on failure, with the error set in the knote, or selres on success.
+ */
+static int
+tty_set_knote_hook(struct knote *kn)
+{
+       uthread_t uth;
+       vfs_context_t ctx;
+       vnode_t vp;
+       kern_return_t kr;
+       struct waitq *wq = NULL;
+       struct waitq_set *old_wqs;
+       struct waitq_set tmp_wqs;
+       uint64_t rsvd, rsvd_arg;
+       uint64_t *rlptr = NULL;
+       int selres = -1;
        struct tty *tp;
+
+       uth = get_bsdthread_info(current_thread());
+
+       ctx = vfs_context_current();
+       vp = (vnode_t)kn->kn_fp->f_fglob->fg_data;
+
+       /*
+        * Reserve a link element to avoid potential allocation under
+        * a spinlock.
+        */
+       rsvd = rsvd_arg = waitq_link_reserve(NULL);
+       rlptr = (void *)&rsvd_arg;
+
+       /*
+        * Trick selrecord into hooking a known waitq set into the device's selinfo
+        * waitq.  Once the link is in place, we can get back into the selinfo from
+        * the waitq and subsequently the tty (see tty_from_waitq).
+        *
+        * We can't use a real waitq set (such as the kqueue's) because wakeups
+        * might happen before we can unlink it.
+        */
+       kr = waitq_set_init(&tmp_wqs, SYNC_POLICY_FIFO | SYNC_POLICY_PREPOST, NULL,
+                       NULL);
+       assert(kr == KERN_SUCCESS);
+
+       /*
+        * Lazy allocate the waitqset to avoid potential allocation under
+        * a spinlock;
+        */
+       waitq_set_lazy_init_link(&tmp_wqs);
+
+       old_wqs = uth->uu_wqset;
+       uth->uu_wqset = &tmp_wqs;
+       /*
+        * FMARK forces selects to always call selrecord, even if data is
+        * available.  See ttselect, ptsselect, ptcselect.
+        *
+        * selres also contains the data currently available in the tty.
+        */
+       selres = VNOP_SELECT(vp, knote_get_seltype(kn) | FMARK, 0, rlptr, ctx);
+       uth->uu_wqset = old_wqs;
+
+       /*
+        * Make sure to cleanup the reserved link - this guards against
+        * drivers that may not actually call selrecord().
+        */
+       waitq_link_release(rsvd);
+       if (rsvd == rsvd_arg) {
+               /*
+                * The driver didn't call selrecord -- there's no tty hooked up so we
+                * can't attach.
+                */
+               knote_set_error(kn, ENOTTY);
+               selres = -1;
+               goto out;
+       }
+
+       /* rlptr may not point to a properly aligned pointer */
+       memcpy(&wq, rlptr, sizeof(void *));
+
+       tp = tty_from_waitq(wq, knote_get_seltype(kn));
+       assert(tp != NULL);
+
+       /*
+        * Take a reference and stash the tty in the knote.
+        */
+       tty_lock(tp);
+       ttyhold(tp);
+       kn->kn_hook = tp;
+       tty_unlock(tp);
+
+out:
+       /*
+        * Cleaning up the wqset will unlink its waitq and clean up any preposts
+        * that occurred as a result of data coming in while the tty was attached.
+        */
+       waitq_set_deinit(&tmp_wqs);
+
+       return selres;
+}
+
+static int
+filt_ttyattach(struct knote *kn, __unused struct kevent_internal_s *kev)
+{
+       int selres = 0;
+       struct tty *tp;
+
+       /*
+        * This function should be called from filt_specattach (spec_vnops.c),
+        * so most of the knote data structure should already be initialized.
+        */
+
+       /* don't support offsets in ttys or drivers that don't use struct tty */
+       if (kn->kn_vnode_use_ofst || !kn->kn_vnode_kqok) {
+               knote_set_error(kn, ENOTSUP);
+               return 0;
+       }
+
+       /*
+        * Connect the struct tty to the knote through the selinfo structure
+        * referenced by the waitq within the selinfo.
+        */
+       selres = tty_set_knote_hook(kn);
+       if (selres < 0) {
+               return 0;
+       }
+
+       /*
+        * Attach the knote to selinfo's klist.
+        */
+       tp = tty_lock_from_knote(kn);
+       if (!tp) {
+               knote_set_error(kn, ENOENT);
+               return 0;
+       }
+
+       switch (knote_get_seltype(kn)) {
+       case FREAD:
+               KNOTE_ATTACH(&tp->t_rsel.si_note, kn);
+               break;
+       case FWRITE:
+               KNOTE_ATTACH(&tp->t_wsel.si_note, kn);
+               break;
+       }
+
+       tty_unlock(tp);
+
+       return selres;
+}
+
+static void
+filt_ttydetach(struct knote *kn)
 {
-        FREE(tp, M_TTYS);
+       struct tty *tp;
+
+       tp = tty_lock_from_knote(kn);
+       if (!tp) {
+               knote_set_error(kn, ENOENT);
+               return;
+       }
+
+       struct selinfo *si = NULL;
+       switch (knote_get_seltype(kn)) {
+       case FREAD:
+               si = &tp->t_rsel;
+               break;
+       case FWRITE:
+               si = &tp->t_wsel;
+               break;
+       /* knote_get_seltype will panic on default */
+       }
+
+       KNOTE_DETACH(&si->si_note, kn);
+       kn->kn_hook = NULL;
+
+       tty_unlock(tp);
+       ttyfree(tp);
+}
+
+static int
+filt_ttyevent(struct knote *kn, long hint)
+{
+       int ret;
+       struct tty *tp;
+       bool revoked = hint & NOTE_REVOKE;
+       hint &= ~NOTE_REVOKE;
+
+       tp = tty_from_knote(kn);
+       if (!tp) {
+               knote_set_error(kn, ENOENT);
+               return 0;
+       }
+
+       if (!hint) {
+               tty_lock(tp);
+       }
+
+       if (revoked) {
+               kn->kn_flags |= EV_EOF | EV_ONESHOT;
+               ret = 1;
+       } else {
+               ret = filt_tty_common(kn, tp);
+       }
+
+       if (!hint) {
+               tty_unlock(tp);
+       }
+
+       return ret;
+}
+
+static int
+filt_ttytouch(struct knote *kn, struct kevent_internal_s *kev)
+{
+       struct tty *tp;
+       int res = 0;
+
+       tp = tty_lock_from_knote(kn);
+       if (!tp) {
+               knote_set_error(kn, ENOENT);
+               return 0;
+       }
+
+       kn->kn_sdata = kev->data;
+       kn->kn_sfflags = kev->fflags;
+
+       if (kn->kn_vnode_kqok) {
+               res = filt_tty_common(kn, tp);
+       }
+
+       tty_unlock(tp);
+
+       return res;
+}
+
+static int
+filt_ttyprocess(struct knote *kn, __unused struct filt_process_s *data, struct kevent_internal_s *kev)
+{
+       struct tty *tp;
+       int res;
+
+       tp = tty_lock_from_knote(kn);
+       if (!tp) {
+               knote_set_error(kn, ENOENT);
+               return 0;
+       }
+
+       res = filt_tty_common(kn, tp);
+
+       if (res) {
+               *kev = kn->kn_kevent;
+               if (kn->kn_flags & EV_CLEAR) {
+                       kn->kn_fflags = 0;
+                       kn->kn_data = 0;
+               }
+       }
+
+       tty_unlock(tp);
+
+       return res;
 }
-#endif /* 0 */
-#endif /* NeXT */