]> git.saurik.com Git - apple/xnu.git/blobdiff - bsd/kern/tty.c
xnu-2782.40.9.tar.gz
[apple/xnu.git] / bsd / kern / tty.c
index 8417816125e71259ccae35d7123c6ab7834f9b6e..2586c482fe01d2f6beb8c2a146ac50f1037e28cf 100644 (file)
@@ -130,7 +130,7 @@ static lck_grp_t    *tty_lck_grp;
 static lck_grp_attr_t  *tty_lck_grp_attr;
 static lck_attr_t      *tty_lck_attr;
 
-static int     ttnread(struct tty *tp);
+__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);
@@ -142,6 +142,9 @@ static void ttyunblock(struct tty *tp);
 static int     ttywflush(struct tty *tp);
 static int     proc_compare(proc_t p1, proc_t p2);
 
+static 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);
 
@@ -339,8 +342,9 @@ int
 ttyopen(dev_t device, struct tty *tp)
 {
        proc_t p = current_proc();
-       struct pgrp * pg, * oldpg;
+       struct pgrp *pg, *oldpg;
        struct session *sessp, *oldsess;
+       struct tty *oldtp;
 
        TTY_LOCK_OWNED(tp);     /* debug assert */
 
@@ -359,15 +363,15 @@ ttyopen(dev_t device, struct tty *tp)
        /*
         * First tty open affter setsid() call makes this tty its controlling
         * tty, if the tty does not already have a session associated with it.
-        * Only do this if the process
         */
-       if (SESS_LEADER(p, sessp) &&                    /* process is session leader */
+       if (SESS_LEADER(p, sessp) &&    /* the process is the session leader */
            sessp->s_ttyvp == NULL &&   /* but has no controlling tty */
-           tp->t_session == NULL ) {           /* and tty not controlling */
+           tp->t_session == NULL ) {   /* and tty not controlling */
                session_lock(sessp);
                if ((sessp->s_flags & S_NOCTTY) == 0) { /* and no O_NOCTTY */
-                       /* Hold on to the reference */
-                       sessp->s_ttyp = tp;     /* XXX NOT A REFERENCE */
+                       oldtp = sessp->s_ttyp;
+                       ttyhold(tp);
+                       sessp->s_ttyp = tp;
                        OSBitOrAtomic(P_CONTROLT, &p->p_flag);
                        session_unlock(sessp);
                        proc_list_lock();
@@ -385,6 +389,8 @@ ttyopen(dev_t device, struct tty *tp)
                                pg_rele(oldpg);
                        if (oldsess != SESSION_NULL)
                                session_rele(oldsess);  
+                       if (NULL != oldtp)
+                               ttyfree(oldtp);
                        tty_lock(tp);
                        goto out;
                }
@@ -954,6 +960,29 @@ ttyoutput(int c, struct tty *tp)
        return (-1);
 }
 
+/*
+ * 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);
+}
+
+/*
+ * 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
@@ -1047,8 +1076,9 @@ ttioctl_locked(struct tty *tp, u_long cmd, caddr_t data, int flag, proc_t p)
 {
        int error = 0;
        struct uthread *ut;
-       struct pgrp * pg, *oldpg;
-       struct session *sessp, * oldsessp;
+       struct pgrp *pg, *oldpg;
+       struct session *sessp, *oldsessp;
+       struct tty *oldtp;
 
        TTY_LOCK_OWNED(tp);     /* debug assert */
 
@@ -1404,7 +1434,9 @@ ttioctl_locked(struct tty *tp, u_long cmd, caddr_t data, int flag, proc_t p)
                tp->t_pgrp = pg;
                proc_list_unlock();
                session_lock(sessp);
-               sessp->s_ttyp = tp;     /* XXX NOT A REFERENCE */
+               oldtp = sessp->s_ttyp;
+               ttyhold(tp);
+               sessp->s_ttyp = tp;
                session_unlock(sessp);
                OSBitOrAtomic(P_CONTROLT, &p->p_flag);
                /* SAFE: All callers drop the lock on return */
@@ -1414,6 +1446,8 @@ ttioctl_locked(struct tty *tp, u_long cmd, caddr_t data, int flag, proc_t p)
                        session_rele(oldsessp);
                if (oldpg != PGRP_NULL)
                        pg_rele(oldpg);
+               if (NULL != oldtp)
+                       ttyfree(oldtp);
                tty_lock(tp);
                break;
 
@@ -1442,6 +1476,15 @@ ttioctl_locked(struct tty *tp, u_long cmd, caddr_t data, int flag, proc_t p)
                        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)) {
+                       error = EPERM;
+                       goto out;
+               }
                proc_list_lock();
                oldpg = tp->t_pgrp;
                tp->t_pgrp = pgrp;
@@ -1498,6 +1541,8 @@ out:
 int
 ttyselect(struct tty *tp, int rw, void *wql, proc_t p)
 {
+       int retval = 0;
+
        if (tp == NULL)
                return (ENXIO);
 
@@ -1505,20 +1550,32 @@ ttyselect(struct tty *tp, int rw, void *wql, proc_t p)
 
        switch (rw) {
        case FREAD:
-               if (ttnread(tp) > 0 || ISSET(tp->t_state, TS_ZOMBIE))
+               if (ISSET(tp->t_state, TS_ZOMBIE)) {
                        return(1);
+               }
+
+               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)) {
-                       return (1);
+               if (ISSET(tp->t_state, TS_ZOMBIE)) {
+                       return(1);
+               }
+
+               if ((tp->t_outq.c_cc <= tp->t_lowat) &&
+                               ISSET(tp->t_state, TS_CONNECTED)) {
+                       retval = tp->t_hiwat - tp->t_outq.c_cc;
+                       break;
                }
+
                selrecord(p, &tp->t_wsel, wql);
                break;
        }
-       return (0);
+       return retval;
 }
 
 
@@ -1545,7 +1602,7 @@ ttselect(dev_t dev, int rw, void *wql, proc_t p)
 /*
  * Locks:      Assumes tp is locked on entry, remains locked on exit
  */
-static int
+__private_extern__ int
 ttnread(struct tty *tp)
 {
        int nread;
@@ -2120,7 +2177,7 @@ read:
                char ibuf[IBUFSIZ];
                int icc;
 
-               icc = min(uio_resid(uio), IBUFSIZ);
+               icc = MIN(uio_resid(uio), IBUFSIZ);
                icc = q_to_b(qp, (u_char *)ibuf, icc);
                if (icc <= 0) {
                        if (first)
@@ -2161,8 +2218,8 @@ slowcase:
                        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;
@@ -2341,7 +2398,7 @@ loop:
                 * leftover from last time.
                 */
                if (cc == 0) {
-                       cc = min(uio_resid(uio), OBUFSIZ);
+                       cc = MIN(uio_resid(uio), OBUFSIZ);
                        cp = obuf;
                        error = uiomove(cp, cc, uio);
                        if (error) {
@@ -3024,22 +3081,57 @@ ttymalloc(void)
                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);
+       return (tp);
 }
 
+/*
+ * Increment the reference count on a tty.
+ */
+static void
+ttyhold(struct tty *tp)
+{
+       TTY_LOCK_OWNED(tp);
+       tp->t_refcnt++;
+}
 
 /*
- * Free a tty structure and its buffers.
- *
- * Locks:      The tty_lock() is assumed to not be held at the time of
- *             the free; this functions destroys the mutex.
+ * 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);
@@ -3077,4 +3169,3 @@ isctty_sp(proc_t p, struct tty  *tp, struct session *sessp)
        return(sessp == tp->t_session && p->p_flag & P_CONTROLT);
 
 }
-