This adds EVFILT_EXCEPT handler for ttys to let kqueue-based poll(2)
detect POLLHUP when pollfd.event == 0.
filt_ttywrite(), and also filt_ptcwrite(), appear to lack HUP detection.
Has this been intentional?
The poll(2) emulation would need the HUP bits if feature similarity to
ttpoll(), and ptcpoll(), is wanted.
The patch also extends the scope of spltty() to cover the checking of
t_cflag and t_state, and modification of kn, in the event filters.
OK?
Index: kern/tty.c
===================================================================
RCS file: src/sys/kern/tty.c,v
retrieving revision 1.171
diff -u -p -r1.171 tty.c
--- kern/tty.c 2 Dec 2021 15:13:49 -0000 1.171
+++ kern/tty.c 11 Dec 2021 13:42:04 -0000
@@ -78,6 +78,7 @@ int filt_ttyread(struct knote *kn, long
void filt_ttyrdetach(struct knote *kn);
int filt_ttywrite(struct knote *kn, long hint);
void filt_ttywdetach(struct knote *kn);
+int filt_ttyexcept(struct knote *kn, long hint);
void ttystats_init(struct itty **, int *, size_t *);
int ttywait_nsec(struct tty *, uint64_t);
int ttysleep_nsec(struct tty *, void *, int, char *, uint64_t);
@@ -1110,6 +1111,13 @@ const struct filterops ttywrite_filtops
.f_event = filt_ttywrite,
};
+const struct filterops ttyexcept_filtops = {
+ .f_flags = FILTEROP_ISFD,
+ .f_attach = NULL,
+ .f_detach = filt_ttyrdetach,
+ .f_event = filt_ttyexcept,
+};
+
int
ttkqfilter(dev_t dev, struct knote *kn)
{
@@ -1126,6 +1134,18 @@ ttkqfilter(dev_t dev, struct knote *kn)
klist = &tp->t_wsel.si_note;
kn->kn_fop = &ttywrite_filtops;
break;
+ case EVFILT_EXCEPT:
+ if (kn->kn_flags & __EV_SELECT) {
+ /* Prevent triggering exceptfds. */
+ return (EPERM);
+ }
+ if ((kn->kn_flags & __EV_POLL) == 0) {
+ /* Disallow usage through kevent(2). */
+ return (EINVAL);
+ }
+ klist = &tp->t_rsel.si_note;
+ kn->kn_fop = &ttyexcept_filtops;
+ break;
default:
return (EINVAL);
}
@@ -1154,18 +1174,19 @@ int
filt_ttyread(struct knote *kn, long hint)
{
struct tty *tp = kn->kn_hook;
- int s;
+ int active, s;
s = spltty();
kn->kn_data = ttnread(tp);
- splx(s);
+ active = (kn->kn_data > 0);
if (!ISSET(tp->t_cflag, CLOCAL) && !ISSET(tp->t_state, TS_CARR_ON)) {
kn->kn_flags |= EV_EOF;
if (kn->kn_flags & __EV_POLL)
kn->kn_flags |= __EV_HUP;
- return (1);
+ active = 1;
}
- return (kn->kn_data > 0);
+ splx(s);
+ return (active);
}
void
@@ -1183,13 +1204,38 @@ int
filt_ttywrite(struct knote *kn, long hint)
{
struct tty *tp = kn->kn_hook;
- int canwrite, s;
+ int active, s;
s = spltty();
kn->kn_data = tp->t_outq.c_cn - tp->t_outq.c_cc;
- canwrite = (tp->t_outq.c_cc <= tp->t_lowat);
+ active = (tp->t_outq.c_cc <= tp->t_lowat);
+ if (!ISSET(tp->t_cflag, CLOCAL) && !ISSET(tp->t_state, TS_CARR_ON)) {
+ kn->kn_flags |= EV_EOF;
+ if (kn->kn_flags & __EV_POLL)
+ kn->kn_flags |= __EV_HUP;
+ active = 1;
+ }
+ splx(s);
+ return (active);
+}
+
+int
+filt_ttyexcept(struct knote *kn, long hint)
+{
+ struct tty *tp = kn->kn_hook;
+ int active = 0;
+ int s;
+
+ s = spltty();
+ if (kn->kn_flags & __EV_POLL) {
+ if (!ISSET(tp->t_cflag, CLOCAL) &&
+ !ISSET(tp->t_state, TS_CARR_ON)) {
+ kn->kn_flags |= __EV_HUP;
+ active = 1;
+ }
+ }
splx(s);
- return (canwrite);
+ return (active);
}
static int