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

Reply via email to