tty: level 2 and other improvements
authorAlan Cox <alan@linux.intel.com>
Wed, 31 Aug 2016 19:10:01 +0000 (20:10 +0100)
committerAlan Cox <alan@linux.intel.com>
Wed, 31 Aug 2016 19:10:01 +0000 (20:10 +0100)
Add what should match the rules for BSD job control. Make sure all
the crap that gets inflicted upon us for this ends up in level2.c
so we don't bloat the smaller platforms.

Do some more preparatory work on the pty support.

Kernel/include/kernel.h
Kernel/include/level2.h
Kernel/level2.c
Kernel/tty.c

index 02e4878..a2cf72f 100644 (file)
@@ -40,8 +40,10 @@ From UZI by Doug Braun and UZI280 by Stefan Nitschke.
 #include "level2.h"
 #else
 
-#define jobcontrol_in(x,y)
-#define jobcontrol_out(x,y)
+#define jobcontrol_in(x,y,z)   0
+#define jobcontrol_out(x,y,z)  0
+#define jobcontrol_ioctl(x,y,z)        0
+
 #define limit_exceeded(x,y) (0)
 #define can_signal(p, sig) \
        (udata.u_ptab->p_uid == (p)->p_uid || super())
index 093a0e7..604605f 100644 (file)
@@ -27,8 +27,9 @@ struct rlimit {
 struct tty;
 
 extern int in_group(uint16_t gid);
-extern void jobcontrol_in(uint8_t minor, struct tty *tty);
-extern void jobcontrol_out(uint8_t minor, struct tty *tty);
+extern uint8_t jobcontrol_in(uint8_t minor, struct tty *tty, usize_t *nread);
+extern uint8_t jobcontrol_out(uint8_t minor, struct tty *tty, usize_t *written);
+extern uint8_t jobcontrol_ioctl(uint8_t minor, struct tty *tty, uarg_t request);
 extern int tcsetpgrp(struct tty *tty, char *data);
 
 /* Platform must implement according to its PATH_MAX and allocators. If
index 15d29aa..7275e9e 100644 (file)
@@ -15,31 +15,114 @@ int in_group(uint16_t gid)
        return 0;
 }
 
-static void jobop(uint8_t minor, uint8_t sig, struct tty *t)
+static int orphan_pgrp(uint16_t pgrp, uint16_t sid)
 {
-       if (!t->pgrp || udata.u_ptab->p_pgrp == t->pgrp
-               || udata.u_ptab->p_tty != minor)
-               return;
+       ptptr p;
+
+       for (p = ptab; p < ptab_end; ++p) {
+               /* A group is not orphan if a process exists that is
+                  - a member of the pgrp
+                  - has a parent that is not a member of the pgrp
+                  - that parent shares the session of the pgrp
+
+                  or in plain English - there is a process managing the group
+                */
+               if (p->p_pgrp == pgrp) {
+                       if (p->p_pptr->p_pgrp != pgrp &&
+                               p->p_pptr->p_session == sid)
+                               return 0;
+               }
+       }
+       return 1;
+}
+
+/* Keep all the process group and tty complexity as far out of the tty layer as we can so it has
+   no impact on a level 1 host */
+
+static uint8_t jobop(uint8_t minor, uint8_t sig, struct tty *t, uint8_t ign)
+{
+       struct s_queue *q = &ttyinq[minor];
+       uint8_t ignored = 0;
+       ptptr p = udata.u_ptab;
+
+       for(;;) {
+               /* If it is dead just return ready - the caller will do the EIO handling */
+               if ((t->flag & TTYF_DEAD) && (sig != SIGTTIN || !q->q_count))
+                       return 0;
+
+               if (!t->pgrp || udata.u_ptab->p_pgrp == t->pgrp
+                       || udata.u_ptab->p_tty != minor)
+                       return 0;       /* Proceed */
 #ifdef DEBUG
-        kprintf("[stop %d %d %d]\n",
-                t->pgrp, udata.u_ptab->p_pgrp, udata.u_ptab->p_tty);
+               kprintf("[stop %d %d %d]\n",
+                       t->pgrp, udata.u_ptab->p_pgrp, udata.u_ptab->p_tty);
 #endif
-       ssig(udata.u_ptab, sig);
-       /* So we halt */
-       psleep(0);
+               if ((udata.u_ptab->p_held & sigmask(sig)) || udata.u_sigvec[sig] == SIG_IGN)
+                       ignored = 1;
+
+               if ((ignored && ign) || orphan_pgrp(p->p_pgrp, p->p_session)) {
+                       udata.u_error = EIO;
+                       return 1;
+               }
+               if (ignored)
+                       return 0;
+
+               /* Check: self or session ? */
+               ssig(udata.u_ptab, sig);
+               /* If we have nothing to deliver we will stop here until we get SIGCONT. If we have
+                  handlers to run first we will return -1, EINTR so the signal is processed */
+               if (chksigs()) {
+                       udata.u_error = EINTR;
+                       return 1;
+               }
+       }
 }
 
-void jobcontrol_in(uint8_t minor, struct tty *t)
+/* For input readign with SIGTTIN blocked gets you an EIO */
+uint8_t jobcontrol_in(uint8_t minor, struct tty *t, usize_t *nread)
 {
-        jobop(minor, SIGTTIN, t);
+       if (!jobop(minor, SIGTTIN, t, 0))
+               return 0;
+       if (*nread == 0) {
+               udata.u_error = EINTR;
+               *nread = (usize_t)-1;
+       }
+       return 1;
 }
 
 
-void jobcontrol_out(uint8_t minor, struct tty *t)
+/* For output ignored stop is taken to mean as write anyway */
+uint8_t jobcontrol_out(uint8_t minor, struct tty *t, usize_t *written)
 {
        if (!(t->termios.c_lflag & TOSTOP))
-               return;
-        jobop(minor, SIGTTOU, t);
+               return 0;
+        if (!jobop(minor, SIGTTOU, t, 1))
+               return 0;
+       if (*written == 0) {
+               udata.u_error = EINTR;
+               *written = (usize_t)-1;
+       }
+       return 1;
+}
+
+/* Keep all the ioctl noise in the level2.c code not anywhere shared with L1 */
+uint8_t jobcontrol_ioctl(uint8_t minor, struct tty *t, uarg_t request)
+{
+       switch(request) {
+       case TCSETSF:
+       case TCSETSW:
+       case TCSETS:
+       case TIOCFLUSH:
+       case TIOCHANGUP:
+       case TIOCOSTOP:
+       case TIOCOSTART:
+       case TIOCSWINSZ:
+       case TIOCSPGRP:
+               /* This should be safe .. but if you spin here bug me 8) */
+               return jobop(minor, SIGTTOU, t, 1);
+       default:
+               return 0;
+       }
 }
 
 int tcsetpgrp(struct tty *t, char *data)       /* data is user pointer */
index 929144d..518edc0 100644 (file)
@@ -33,11 +33,10 @@ int tty_read(uint8_t minor, uint8_t rawflag, uint8_t flag)
        nread = 0;
        while (nread < udata.u_count) {
                for (;;) {
-                       if ((t->flag & TTYF_DEAD)&&(!q->q_count)) {
-                               udata.u_error = ENXIO;
-                               return -1;
-                        }
-                        jobcontrol_in(minor, t);
+                        if (jobcontrol_in(minor, t, &nread))
+                               return nread;
+                       if ((t->flag & TTYF_DEAD) && (!q->q_count))
+                               goto dead;
                        if (remq(q, &c)) {
                                if (udata.u_sysio)
                                        *udata.u_base = c;
@@ -78,8 +77,11 @@ int tty_read(uint8_t minor, uint8_t rawflag, uint8_t flag)
 out:
        wakeup(&q->q_count);
        return nread;
-}
 
+dead:
+        udata.u_error = ENXIO;
+       return -1;
+}
 
 int tty_write(uint8_t minor, uint8_t rawflag, uint8_t flag)
 {
@@ -94,15 +96,16 @@ int tty_write(uint8_t minor, uint8_t rawflag, uint8_t flag)
 
        while (udata.u_count-- != 0) {
                for (;;) {      /* Wait on the ^S/^Q flag */
+                       if (jobcontrol_out(minor, t, &written))
+                               return written;
                        if (t->flag & TTYF_DEAD) {
-                               udata.u_error = ENXIO;
-                               return -1;
-                        }
+                               udata.u_error = ENXIO;
+                               return -1;
+                       }
                        if (!(t->flag & TTYF_STOP))
                                break;
                        if (psleep_flags_io(&t->flag, flag, &written))
                                return written;
-                        jobcontrol_out(minor, t);
                }
                if (!(t->flag & TTYF_DISCARD)) {
                        if (udata.u_sysio)
@@ -124,6 +127,7 @@ int tty_write(uint8_t minor, uint8_t rawflag, uint8_t flag)
        return written;
 }
 
+
 int tty_open(uint8_t minor, uint16_t flag)
 {
        struct tty *t;
@@ -224,11 +228,13 @@ int tty_ioctl(uint8_t minor, uarg_t request, char *data)
                return -1;
        }
         t = &ttydata[minor];
+        if (jobcontrol_ioctl(minor, t, request))
+               return -1;
        if (t->flag & TTYF_DEAD) {
                udata.u_error = ENXIO;
                return -1;
         }
-        jobcontrol_in(minor, t);
+
        switch (request) {
        case TCGETS:
                return uput(&t->termios, data, sizeof(struct termios));
@@ -466,8 +472,7 @@ void tty_carrier_drop(uint8_t minor)
 
 void tty_carrier_raise(uint8_t minor)
 {
-        if (ttydata[minor].termios.c_cflag & HUPCL)
-                wakeup(&ttydata[minor].termios.c_cflag);
+       wakeup(&ttydata[minor].termios.c_cflag);
 }
 
 /*
@@ -475,6 +480,9 @@ void tty_carrier_raise(uint8_t minor)
  */
 
 #ifdef CONFIG_DEV_PTY
+
+static uint8_t ptyusers[PTY_PAIR];
+
 int ptty_open(uint8_t minor, uint16_t flag)
 {
        return tty_open(minor + PTY_OFFSET, flag);
@@ -502,11 +510,20 @@ int ptty_ioctl(uint8_t minor, uint16_t request, char *data)
 
 int pty_open(uint8_t minor, uint16_t flag)
 {
-       return tty_open(minor + PTY_OFFSET, flag);
+       int r = tty_open(minor + PTY_OFFSET, flag | O_NOCTTY | O_NDELAY);
+       if (r == 0) {
+               if (!ptyusers[minor])
+                       tty_carrier_raise(minor + PTY_OFFSET);
+               ptyusers[minor]++;
+       }
+       return r;
 }
 
 int pty_close(uint8_t minor)
 {
+       ptyusers[minor]--;
+       if (ptyusers[minor] == 0)
+               tty_carrider_drop(minor + PTY_OFFSET);
        return tty_close(minor + PTY_OFFSET);
 }