/*
- * Copyright (c) 1997-2017 Apple Inc. All rights reserved.
+ * Copyright (c) 1997-2019 Apple Inc. All rights reserved.
*
* @APPLE_OSREFERENCE_LICENSE_HEADER_START@
*
static int isctty(proc_t p, struct tty *tp);
static int isctty_sp(proc_t p, struct tty *tp, struct session *sessp);
+__private_extern__ void termios32to64(struct termios32 *in, struct user_termios *out);
+__private_extern__ void termios64to32(struct user_termios *in, struct termios32 *out);
+
/*
* Table with character classes and parity. The 8th bit indicates parity,
* the 7th bit indicates the character is an alphameric or underscore (for
#define I_HIGH_WATER (TTYHOG - 2 * 256) /* XXX */
#define I_LOW_WATER ((TTYHOG - 2 * 256) * 7 / 8) /* XXX */
-static void
+__private_extern__ void
termios32to64(struct termios32 *in, struct user_termios *out)
{
out->c_iflag = (user_tcflag_t)in->c_iflag;
out->c_ospeed = (user_speed_t)in->c_ospeed;
}
-static void
+__private_extern__ 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;
+ out->c_iflag = (uint32_t)in->c_iflag;
+ out->c_oflag = (uint32_t)in->c_oflag;
+ out->c_cflag = (uint32_t)in->c_cflag;
+ out->c_lflag = (uint32_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;
+ out->c_ispeed = (uint32_t)MIN(in->c_ispeed, UINT32_MAX);
+ out->c_ospeed = (uint32_t)MIN(in->c_ospeed, UINT32_MAX);
}
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);
- /*
- * 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
*
case TIOCSCONS: {
/* Set current console device to this line */
data = (caddr_t) &bogusData;
-
- /* No break - Fall through to BSD code */
}
+ OS_FALLTHROUGH;
case TIOCCONS: { /* become virtual console */
if (*(int *)data) {
if (constty && constty != 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;
* process group.
*
* ttwakeup() isn't called because the readers aren't getting
- * woken up becuse there is something to read but to force
+ * woken up because 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
case TIOCGDRAINWAIT:
*(int *)data = tp->t_timeout / hz;
break;
+ case TIOCREVOKE:
+ SET(tp->t_state, TS_REVOKE);
+ tp->t_gen++;
+ /*
+ * At this time, only this wait channel is woken up as only
+ * ttread has been problematic. It is possible we may need
+ * to add wake up other tty wait addresses as well.
+ */
+ wakeup(TSA_HUP_OR_INPUT(tp));
+ break;
+ case TIOCREVOKECLEAR:
+ CLR(tp->t_state, TS_REVOKE);
+ break;
default:
error = ttcompat(tp, cmd, data, flag, p);
goto out;
}
/*
- * Signal the process if it's in the background.
+ * Signal the process if it's in the background. If the terminal is
+ * getting revoked, everybody is in the background.
*/
- if (isbackground(p, tp)) {
+ if (isbackground(p, tp) || ISSET(tp->t_state, TS_REVOKE)) {
if ((p->p_sigignore & sigmask(SIGTTIN)) ||
(ut->uu_sigmask & sigmask(SIGTTIN)) ||
p->p_lflag & P_LPPWAIT) {
int m = cc[VMIN];
long t = cc[VTIME];
struct timeval timecopy;
- struct timeval etime = {0, 0}; /* protected by !has_etime */
+ struct timeval etime = {.tv_sec = 0, .tv_usec = 0}; /* protected by !has_etime */
/*
* Check each of the four combinations.
goto read;
}
microuptime(&timecopy);
- if (!has_etime) {
- /* first character, start timer */
+ if (!has_etime || qp->c_cc > last_cc) {
+ /* first character or got a character, start timer */
has_etime = 1;
etime.tv_sec = t / 1000000;
- etime.tv_usec = (t - (etime.tv_sec * 1000000));
- timeradd(&etime, &timecopy, &etime);
-
- slp = t;
- } else if (qp->c_cc > last_cc) {
- /* got a character, restart timer */
-
- etime.tv_sec = t / 1000000;
- etime.tv_usec = (t - (etime.tv_sec * 1000000));
+ etime.tv_usec =
+ (__darwin_suseconds_t)(t - (etime.tv_sec * 1000000));
timeradd(&etime, &timecopy, &etime);
slp = t;
has_etime = 1;
etime.tv_sec = t / 1000000;
- etime.tv_usec = (t - (etime.tv_sec * 1000000));
+ etime.tv_usec =
+ (__darwin_suseconds_t)(t - (etime.tv_sec * 1000000));
timeradd(&etime, &timecopy, &etime);
slp = t;
for (;;) {
char ibuf[IBUFSIZ];
int icc;
+ ssize_t size = uio_resid(uio);
+ if (size < 0) {
+ error = ERANGE;
+ break;
+ }
- icc = MIN(uio_resid(uio), IBUFSIZ);
+ icc = (int)MIN(size, IBUFSIZ);
icc = q_to_b(qp, (u_char *)ibuf, icc);
if (icc <= 0) {
if (first) {
* leftover from last time.
*/
if (cc == 0) {
- cc = MIN(uio_resid(uio), OBUFSIZ);
+ ssize_t size = uio_resid(uio);
+ if (size < 0) {
+ error = ERANGE;
+ break;
+ }
+ cc = (int)MIN((size_t)size, OBUFSIZ);
cp = obuf;
error = uiomove(cp, cc, uio);
if (error) {
if (!ISSET(tp->t_oflag, OPOST)) {
ce = cc;
} else {
- ce = cc - scanc((u_int)cc, (u_char *)cp,
- char_type, CCLASSMASK);
+ ce = (int)((size_t)cc - scanc((size_t)cc,
+ (u_char *)cp, char_type, CCLASSMASK));
/*
* If ce is zero, then we're processing
* a special character through ttyoutput.
(void)ttyoutput(c, tp);
}
+static void
+ttwakeup_knote(struct selinfo *sip, long hint)
+{
+ if ((sip->si_flags & SI_KNPOSTING) == 0) {
+ sip->si_flags |= SI_KNPOSTING;
+ KNOTE(&sip->si_note, hint);
+ sip->si_flags &= ~SI_KNPOSTING;
+ }
+}
+
/*
* Wake up any readers on a tty.
TTY_LOCK_OWNED(tp); /* debug assert */
selwakeup(&tp->t_rsel);
- KNOTE(&tp->t_rsel.si_note, 1);
+ ttwakeup_knote(&tp->t_rsel, 0);
if (ISSET(tp->t_state, TS_ASYNC)) {
/*
* XXX: Callers may not revalidate it the tty is closed
if (tp->t_outq.c_cc <= tp->t_lowat) {
selwakeup(&tp->t_wsel);
- KNOTE(&tp->t_wsel.si_note, 1);
+ ttwakeup_knote(&tp->t_wsel, 0);
}
if (ISSET(tp->t_state, TS_BUSY | TS_SO_OCOMPLETE) ==
TS_SO_OCOMPLETE && tp->t_outq.c_cc == 0) {
void
ttsetwater(struct tty *tp)
{
- int cps;
+ speed_t cps;
unsigned int x;
TTY_LOCK_OWNED(tp); /* debug assert */
#define CLAMP(x, h, l) ((x) > h ? h : ((x) < l) ? l : (x))
cps = tp->t_ospeed / 10;
- tp->t_lowat = x = CLAMP(cps / 2, TTMAXLOWAT, TTMINLOWAT);
+ static_assert(TTMAXLOWAT <= UINT_MAX, "max low water fits in unsigned int");
+ static_assert(TTMINLOWAT <= UINT_MAX, "min low water fits in unsigned int");
+ tp->t_lowat = x = (unsigned int)CLAMP(cps / 2, TTMAXLOWAT, TTMINLOWAT);
x += cps;
x = CLAMP(x, TTMAXHIWAT, TTMINHIWAT);
tp->t_hiwat = roundup(x, CBSIZE);
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",
state,
(long)utime.tv_sec, utime.tv_usec / 10000,
(long)stime.tv_sec, stime.tv_usec / 10000);
+
+ proc_rele(pick);
tp->t_rocount = 0;
}
TTY_LOCK_OWNED(tp);
+ if (tp->t_state & TS_REVOKE) {
+ return ERESTART;
+ }
+
gen = tp->t_gen;
/* Use of msleep0() avoids conversion timo/timespec/timo */
error = msleep0(chan, &tp->t_lock, pri, wmesg, timo, (int (*)(int))0);
}
-static int filt_ttyattach(struct knote *kn, struct kevent_internal_s *kev);
+static int filt_ttyattach(struct knote *kn, struct kevent_qos_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);
+static int filt_ttytouch(struct knote *kn, struct kevent_qos_s *kev);
+static int filt_ttyprocess(struct knote *kn, struct kevent_qos_s *kev);
SECURITY_READ_ONLY_EARLY(struct filterops) tty_filtops = {
.f_isfd = 1,
* or written.
*/
static int
-filt_tty_common(struct knote *kn, struct tty *tp)
+filt_tty_common(struct knote *kn, struct kevent_qos_s *kev, struct tty *tp)
{
int retval = 0;
+ int64_t data = 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);
+ switch (kn->kn_filter) {
+ case EVFILT_READ:
+ /*
+ * ttnread can change the tty state,
+ * hence must be done upfront, before any other check.
+ */
+ data = ttnread(tp);
+ retval = (data != 0);
break;
- case FWRITE:
+ case EVFILT_WRITE:
if ((tp->t_outq.c_cc <= tp->t_lowat) &&
(tp->t_state & TS_CONNECTED)) {
- retval = tp->t_hiwat - tp->t_outq.c_cc;
+ data = tp->t_hiwat - tp->t_outq.c_cc;
+ retval = (data != 0);
}
break;
+ default:
+ panic("tty kevent: unexpected filter: %d, kn = %p, tty = %p",
+ kn->kn_filter, kn, tp);
+ 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.
* (kn->kn_data >= kn->kn_sdata) : kn->kn_data;
*/
+ if (tp->t_state & TS_ZOMBIE) {
+ kn->kn_flags |= EV_EOF;
+ }
+ if (kn->kn_flags & EV_EOF) {
+ retval = 1;
+ }
+ if (retval && kev) {
+ knote_fill_kevent(kn, kev, data);
+ }
+
return retval;
}
return (struct tty *)kn->kn_hook;
}
-/*
- * Try to lock the TTY structure associated with a knote.
- *
- * On success, this function returns a locked TTY structure. Otherwise, NULL is
- * returned.
- */
-__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.
*
uth = get_bsdthread_info(current_thread());
ctx = vfs_context_current();
- vp = (vnode_t)kn->kn_fp->f_fglob->fg_data;
+ vp = (vnode_t)kn->kn_fp->fp_glob->fg_data;
/*
* Reserve a link element to avoid potential allocation under
}
static int
-filt_ttyattach(struct knote *kn, __unused struct kevent_internal_s *kev)
+filt_ttyattach(struct knote *kn, __unused struct kevent_qos_s *kev)
{
int selres = 0;
struct tty *tp;
/*
* Attach the knote to selinfo's klist.
*/
- tp = tty_lock_from_knote(kn);
- if (!tp) {
- knote_set_error(kn, ENOENT);
- return 0;
- }
+ tp = tty_from_knote(kn);
+ tty_lock(tp);
- switch (knote_get_seltype(kn)) {
- case FREAD:
+ switch (kn->kn_filter) {
+ case EVFILT_READ:
KNOTE_ATTACH(&tp->t_rsel.si_note, kn);
break;
- case FWRITE:
+ case EVFILT_WRITE:
KNOTE_ATTACH(&tp->t_wsel.si_note, kn);
break;
+ default:
+ panic("invalid knote %p attach, filter: %d", kn, kn->kn_filter);
}
tty_unlock(tp);
static void
filt_ttydetach(struct knote *kn)
{
- struct tty *tp;
+ struct tty *tp = tty_from_knote(kn);
- tp = tty_lock_from_knote(kn);
- if (!tp) {
- knote_set_error(kn, ENOENT);
- return;
- }
+ tty_lock(tp);
- struct selinfo *si = NULL;
- switch (knote_get_seltype(kn)) {
- case FREAD:
- si = &tp->t_rsel;
+ switch (kn->kn_filter) {
+ case EVFILT_READ:
+ KNOTE_DETACH(&tp->t_rsel.si_note, kn);
break;
- case FWRITE:
- si = &tp->t_wsel;
+ case EVFILT_WRITE:
+ KNOTE_DETACH(&tp->t_wsel.si_note, kn);
+ break;
+ default:
+ panic("invalid knote %p detach, filter: %d", kn, kn->kn_filter);
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)
{
+ struct tty *tp = tty_from_knote(kn);
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);
- }
+ TTY_LOCK_OWNED(tp);
- if (revoked) {
+ if (hint & NOTE_REVOKE) {
kn->kn_flags |= EV_EOF | EV_ONESHOT;
ret = 1;
} else {
- ret = filt_tty_common(kn, tp);
- }
-
- if (!hint) {
- tty_unlock(tp);
+ ret = filt_tty_common(kn, NULL, tp);
}
return ret;
}
static int
-filt_ttytouch(struct knote *kn, struct kevent_internal_s *kev)
+filt_ttytouch(struct knote *kn, struct kevent_qos_s *kev)
{
- struct tty *tp;
+ struct tty *tp = tty_from_knote(kn);
int res = 0;
- tp = tty_lock_from_knote(kn);
- if (!tp) {
- knote_set_error(kn, ENOENT);
- return 0;
- }
+ tty_lock(tp);
kn->kn_sdata = kev->data;
kn->kn_sfflags = kev->fflags;
if (kn->kn_vnode_kqok) {
- res = filt_tty_common(kn, tp);
+ res = filt_tty_common(kn, NULL, tp);
}
tty_unlock(tp);
}
static int
-filt_ttyprocess(struct knote *kn, __unused struct filt_process_s *data, struct kevent_internal_s *kev)
+filt_ttyprocess(struct knote *kn, struct kevent_qos_s *kev)
{
- struct tty *tp;
+ struct tty *tp = tty_from_knote(kn);
int res;
- tp = tty_lock_from_knote(kn);
- if (!tp) {
- knote_set_error(kn, ENOENT);
- return 0;
- }
-
- res = filt_tty_common(kn, tp);
+ tty_lock(tp);
- if (res) {
- *kev = kn->kn_kevent;
- if (kn->kn_flags & EV_CLEAR) {
- kn->kn_fflags = 0;
- kn->kn_data = 0;
- }
- }
+ res = filt_tty_common(kn, kev, tp);
tty_unlock(tp);