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 */
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;
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)
{
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)
return written;
}
+
int tty_open(uint8_t minor, uint16_t flag)
{
struct tty *t;
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));
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);
}
/*
*/
#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);
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);
}