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);
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);
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 */
/*
* 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();
pg_rele(oldpg);
if (oldsess != SESSION_NULL)
session_rele(oldsess);
+ if (NULL != oldtp)
+ ttyfree(oldtp);
tty_lock(tp);
goto out;
}
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
{
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 */
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 */
session_rele(oldsessp);
if (oldpg != PGRP_NULL)
pg_rele(oldpg);
+ if (NULL != oldtp)
+ ttyfree(oldtp);
tty_lock(tp);
break;
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;
int
ttyselect(struct tty *tp, int rw, void *wql, proc_t p)
{
+ int retval = 0;
+
if (tp == NULL)
return (ENXIO);
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;
}
/*
* Locks: Assumes tp is locked on entry, remains locked on exit
*/
-static int
+__private_extern__ int
ttnread(struct tty *tp)
{
int nread;
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)
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;
* 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) {
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);
return(sessp == tp->t_session && p->p_flag & P_CONTROLT);
}
-