/* * Process a write call on a tty device. */ static int tty_write(file_t file, char *buf, size_t *nbyte, int blkno) { struct tty *tp = file->priv; size_t remain, count = 0; unsigned char c; /* must be char (not int) for BIG ENDIAN */ DPRINTF(("tty_write\n")); remain = *nbyte; while (remain > 0) { if (tp->t_outq.tq_count > TTYQ_HIWAT) { tty_start(tp); if (tp->t_outq.tq_count <= TTYQ_HIWAT) continue; tp->t_state |= TS_ASLEEP; sched_sleep(&tp->t_output); continue; } if (umem_copyin(buf, &c, 1)) return EFAULT; tty_output(c, tp); buf++; remain--; count++; } tty_start(tp); *nbyte = count; return 0; }
static void watcher(void *arg, int fd, int events) { int len = 0; char buf[EVENT_SIZE], name[42]; tty_node_t *entry; struct inotify_event *notified = (struct inotify_event *)buf; while ((len = read(fd, buf, sizeof(buf)))) { if (-1 == len) { if (errno == EINVAL) setup(); if (errno == EINTR) continue; break; /* Likely EAGAIN */ } snprintf(name, sizeof(name), "/dev/%s", notified->name); entry = tty_find(name); if (entry && tty_enabled(&entry->data)) { if (notified->mask & IN_CREATE) tty_start(&entry->data); else if (entry->data.pid) tty_stop(&entry->data); } } }
/* * Process a write call on a tty device. */ int tty_write(struct tty *tp, struct uio *uio, size_t *size) { u_char c; DPRINTF(TTYDB_CORE, ("tty_write\n")); size_t total = 0; for (int i = 0; i < uio->iovcnt; ++i) { size_t count = 0; char *buf = uio->iov[i].iov_base; size_t nbyte = uio->iov[i].iov_len; while (nbyte > 0) { if (tp->t_outq.tq_count > TTYQ_HIWAT) { tty_start(tp); if (tp->t_outq.tq_count <= TTYQ_HIWAT) continue; tp->t_state |= TS_ASLEEP; sem_wait(&tp->t_output); continue; } if (copyin(buf, &c, 1)) return -EFAULT; tty_output((int)c, tp); ++buf; --nbyte; ++count; } total += count; } tty_start(tp); *size = total; return 0; }
/* * Flush tty read and/or write queues, notifying anyone waiting. */ static void tty_flush(struct tty *tp, int rw) { DPRINTF(("tty_flush rw=%d\n", rw)); if (rw & FREAD) { while (ttyq_getc(&tp->t_canq) != -1) ; while (ttyq_getc(&tp->t_rawq) != -1) ; sched_wakeup(&tp->t_input); } if (rw & FWRITE) { tp->t_state &= ~TS_TTSTOP; tty_start(tp); } }
/* * Flush tty read and/or write queues, notifying anyone waiting. */ static void tty_flush(struct tty *tp, int rw) { DPRINTF(TTYDB_CORE, ("tty_flush rw=%d\n", rw)); if (rw & FREAD) { while (tty_getc(&tp->t_canq) != -1) continue; while (tty_getc(&tp->t_rawq) != -1) continue; sem_post(&tp->t_input); } if (rw & FWRITE) { tp->t_state &= ~TS_TTSTOP; tty_start(tp); } }
/* * Ioctls for all tty devices. */ int tty_ioctl(struct tty *tp, u_long cmd, void *data) { struct tty_queue *qp; switch (cmd) { case TCGETS: if (copyout(&tp->t_termios, data, sizeof(struct termios))) return -EFAULT; break; case TCSETSW: case TCSETSF: tty_wait(tp); if (cmd == TCSETSF) tty_flush(tp, FREAD); /* FALLTHROUGH */ case TCSETS: if (copyin(data, &tp->t_termios, sizeof(struct termios))) return -EFAULT; break; case TIOCSPGRP: /* set pgrp of tty */ if (copyin(data, &tp->t_pgid, sizeof(pid_t))) return -EFAULT; break; case TIOCGPGRP: if (copyout(&tp->t_pgid, data, sizeof(pid_t))) return -EFAULT; break; #ifdef TIOCFLUSH case TIOCFLUSH: { int flags; if (copyin(data, &flags, sizeof(flags))) return -EFAULT; if (flags == 0) flags = FREAD | FWRITE; else flags &= FREAD | FWRITE; tty_flush(tp, flags); break; } #endif #ifdef TIOCSTART case TIOCSTART: if (tp->t_state & TS_TTSTOP) { tp->t_state &= ~TS_TTSTOP; tty_start(tp); } break; #endif #ifdef TIOCSTOP case TIOCSTOP: if (!(tp->t_state & TS_TTSTOP)) { tp->t_state |= TS_TTSTOP; } break; #endif case TIOCGWINSZ: if (copyout(&tp->t_winsize, data, sizeof(struct winsize))) return -EFAULT; break; case TIOCSWINSZ: if (copyin(data, &tp->t_winsize, sizeof(struct winsize))) return -EFAULT; break; #ifdef TIOCSETSIGT case TIOCSETSIGT: /* Prex */ if (copyin(data, &tp->t_pid, sizeof(pid_t))) return -EFAULT; break; #endif case TIOCINQ: qp = (tp->t_lflag & ICANON) ? &tp->t_canq : &tp->t_rawq; if (copyout(&qp->tq_count, data, sizeof(int))) return -EFAULT; break; case TIOCOUTQ: if (copyout(&tp->t_outq.tq_count, data, sizeof(int))) return -EFAULT; break; } return 0; }
/* * Process input of a single character received on a tty. * echo if required. * This may be called with interrupt level. */ void tty_input(int c, struct tty *tp) { unsigned char *cc; tcflag_t iflag, lflag; int sig = -1; DPRINTF(TTYDB_CORE, ("tty_input: %d\n", c)); // Reload power management timer */ pm_notify(PME_USER_ACTIVITY); lflag = tp->t_lflag; iflag = tp->t_iflag; cc = tp->t_cc; #if defined(DEBUG) && defined(CONFIG_KD) if (c == cc[VDDB]) { kd_enter(); tty_flush(tp, FREAD | FWRITE); goto endcase; } #endif /* !CONFIG_KD*/ /* IGNCR, ICRNL, INLCR */ if (c == '\r') { if (iflag & IGNCR) goto endcase; else if (iflag & ICRNL) c = '\n'; } else if (c == '\n' && (iflag & INLCR)) { c = '\r'; } if (iflag & IXON) { /* stop (^S) */ if (c == cc[VSTOP]) { if (!(tp->t_state & TS_TTSTOP)) { tp->t_state |= TS_TTSTOP; return; } if (c != cc[VSTART]) return; /* if VSTART == VSTOP then toggle */ goto endcase; } /* start (^Q) */ if (c == cc[VSTART]) goto restartoutput; } if (lflag & ICANON) { /* erase (^H / ^?) or backspace */ if (c == cc[VERASE] || c == '\b') { if (!ttyq_empty(&tp->t_rawq)) tty_rubout(tty_unputc(&tp->t_rawq), tp); goto endcase; } /* kill (^U) */ if (c == cc[VKILL]) { while (!ttyq_empty(&tp->t_rawq)) tty_rubout(tty_unputc(&tp->t_rawq), tp); goto endcase; } } if (lflag & ISIG) { /* quit (^C) */ if (c == cc[VINTR] || c == cc[VQUIT]) { if (!(lflag & NOFLSH)) { tp->t_state |= TS_ISIG; tty_flush(tp, FREAD | FWRITE); } tty_echo(c, tp); sig = (c == cc[VINTR]) ? SIGINT : SIGQUIT; goto endcase; } /* suspend (^Z) */ if (c == cc[VSUSP]) { if (!(lflag & NOFLSH)) { tp->t_state |= TS_ISIG; tty_flush(tp, FREAD | FWRITE); } tty_echo(c, tp); sig = SIGTSTP; goto endcase; } } /* * Check for input buffer overflow */ if (ttyq_full(&tp->t_rawq)) { tty_flush(tp, FREAD | FWRITE); goto endcase; } tty_putc(c, &tp->t_rawq); if (lflag & ICANON) { if (c == '\n' || c == cc[VEOF] || c == cc[VEOL]) { tty_catq(&tp->t_rawq, &tp->t_canq); sem_post(&tp->t_input); } } else { sem_post(&tp->t_input); } if (lflag & ECHO) tty_echo(c, tp); endcase: /* * IXANY means allow any character to restart output. */ if ((tp->t_state & TS_TTSTOP) && (iflag & IXANY) == 0 && cc[VSTART] != cc[VSTOP]) return; restartoutput: tp->t_state &= ~TS_TTSTOP; if (sig != -1) { if (tp->t_pid) { tp->t_signo = sig; sched_dpc(&tp->t_dpc, &tty_signal, tp); } } tty_start(tp); }
/* * Ioctls for all tty devices. */ static int tty_ioctl(file_t file, u_long cmd, void *data) { struct tty *tp = file->priv; int flags; struct tty_queue *qp; switch (cmd) { case TIOCGETA: if (umem_copyout(&tp->t_termios, data, sizeof(struct termios))) return EFAULT; break; case TIOCSETAW: case TIOCSETAF: tty_wait(tp); if (cmd == TIOCSETAF) tty_flush(tp, FREAD); /* FALLTHROUGH */ case TIOCSETA: if (umem_copyin(data, &tp->t_termios, sizeof(struct termios))) return EFAULT; break; case TIOCSPGRP: /* set pgrp of tty */ if (umem_copyin(data, &tp->t_pgid, sizeof(pid_t))) return EFAULT; break; case TIOCGPGRP: if (umem_copyout(&tp->t_pgid, data, sizeof(pid_t))) return EFAULT; break; case TIOCFLUSH: if (umem_copyin(data, &flags, sizeof(flags))) return EFAULT; break; if (flags == 0) flags = FREAD | FWRITE; else flags &= FREAD | FWRITE; tty_flush(tp, flags); break; case TIOCSTART: if (tp->t_state & TS_TTSTOP) { tp->t_state &= ~TS_TTSTOP; tty_start(tp); } break; case TIOCSTOP: if (!(tp->t_state & TS_TTSTOP)) { tp->t_state |= TS_TTSTOP; } break; case TIOCGWINSZ: if (umem_copyout(&tp->t_winsize, data, sizeof(struct winsize))) return EFAULT; break; case TIOCSWINSZ: if (umem_copyin(&tp->t_winsize, data, sizeof(struct winsize))) return EFAULT; break; case TIOCSETSIGT: if (umem_copyin(data, &sig_task, sizeof(task_t))) return EFAULT; break; case TIOCINQ: qp = (tp->t_lflag & ICANON) ? &tp->t_canq : &tp->t_rawq; if (umem_copyout(&qp->tq_count, data, sizeof(int))) return EFAULT; break; case TIOCOUTQ: if (umem_copyout(&tp->t_outq.tq_count, data, sizeof(int))) return EFAULT; break; } return 0; }
/* * Process input of a single character received on a tty. * echo if required. * This may be called with interrupt level. */ void tty_input(int c, struct tty *tp) { unsigned char *cc; tcflag_t iflag, lflag; int sig = -1; #ifdef CONFIG_CPUFREQ /* Reload power management timer */ pm_active(); #endif lflag = tp->t_lflag; iflag = tp->t_iflag; cc = tp->t_cc; /* IGNCR, ICRNL, INLCR */ if (c == '\r') { if (iflag & IGNCR) goto endcase; else if (iflag & ICRNL) c = '\n'; } else if (c == '\n' && (iflag & INLCR)) c = '\r'; if (iflag & IXON) { /* stop (^S) */ if (c == cc[VSTOP]) { if (!(tp->t_state & TS_TTSTOP)) { tp->t_state |= TS_TTSTOP; return; } if (c != cc[VSTART]) return; /* if VSTART == VSTOP then toggle */ goto endcase; } /* start (^Q) */ if (c == cc[VSTART]) goto restartoutput; } if (lflag & ICANON) { /* erase (^H / ^?) or backspace */ if (c == cc[VERASE] || c == '\b') { if (!ttyq_empty(&tp->t_rawq)) { ttyq_unputc(&tp->t_rawq); tty_rubout(tp); } goto endcase; } /* kill (^U) */ if (c == cc[VKILL]) { while (!ttyq_empty(&tp->t_rawq)) { ttyq_unputc(&tp->t_rawq); tty_rubout(tp); } goto endcase; } } if (lflag & ISIG) { /* quit (^C) */ if (c == cc[VINTR] || c == cc[VQUIT]) { if (!(lflag & NOFLSH)) tty_flush(tp, FREAD | FWRITE); tty_echo(c, tp); sig = (c == cc[VINTR]) ? SIGINT : SIGQUIT; goto endcase; } /* suspend (^Z) */ if (c == cc[VSUSP]) { if (!(lflag & NOFLSH)) tty_flush(tp, FREAD | FWRITE); tty_echo(c, tp); sig = SIGTSTP; goto endcase; } } /* * Check for input buffer overflow */ if (ttyq_full(&tp->t_rawq)) { tty_flush(tp, FREAD | FWRITE); goto endcase; } ttyq_putc(c, &tp->t_rawq); if (lflag & ICANON) { if (c == '\n' || c == cc[VEOF] || c == cc[VEOL]) { tty_catq(&tp->t_rawq, &tp->t_canq); sched_wakeup(&tp->t_input); } } else sched_wakeup(&tp->t_input); if (lflag & ECHO) tty_echo(c, tp); endcase: /* * IXANY means allow any character to restart output. */ if ((tp->t_state & TS_TTSTOP) && (iflag & IXANY) == 0 && cc[VSTART] != cc[VSTOP]) return; restartoutput: tp->t_state &= ~TS_TTSTOP; if (sig != -1) { if (sig_task) exception_post(sig_task, sig); } tty_start(tp); }
/* * start an editing session: process the EDIT/VIEW message * if view == 1, text will be viewed, else edited */ void message_edit __P4 (char *,text, int,msglen, char,view, char,builtin) { char tmpname[BUFSIZE], command_str[BUFSIZE], buf[BUFSIZE]; char *errdesc = "#warning: protocol error (message_edit, no %s)\n"; int tmpfd, i, childpid; unsigned int key; editsess *s; char *editor, *descr; char *args[4]; int waitforeditor; status(1); args[0] = "/bin/sh"; args[1] = "-c"; args[2] = command_str; args[3] = 0; if (view) { key = (unsigned int)-1; i = 0; } else { if (text[0] != 'M') { tty_printf(errdesc, "M"); free(text); return; } for (i = 1; i < msglen && isdigit(text[i]); i++) ; if (text[i++] != '\n' || i >= msglen) { tty_printf(errdesc, "\\n"); free(text); return; } key = strtoul(text + 1, NULL, 10); } descr = text + i; while (i < msglen && text[i] != '\n') i++; if (i >= msglen) { tty_printf(errdesc, "desc"); free(text); return; } text[i++] = '\0'; sprintf(tmpname, "/tmp/powwow.%u.%d%d", key, getpid(), abs(rand()) >> 8); if ((tmpfd = open(tmpname, O_WRONLY | O_CREAT, 0600)) < 0) { errmsg("create temp edit file"); free(text); return; } if (write(tmpfd, text + i, msglen - i) < msglen - i) { errmsg("write to temp edit file"); free(text); close(tmpfd); return; } close(tmpfd); s = (editsess*)malloc(sizeof(editsess)); if (!s) { errmsg("malloc"); return; } s->ctime = time((time_t*)NULL); s->oldsize = msglen - i; s->key = key; s->fd = (view || builtin) ? -1 : tcp_fd; /* MUME doesn't expect a reply. */ s->cancel = 0; s->descr = my_strdup(descr); s->file = my_strdup(tmpname); free(text); /* send a edit_start message (if wanted) */ if ((!edit_sess) && (*edit_start)) { error = 0; parse_instruction(edit_start, 0, 0, 1); history_done = 0; } if (view) { if (!(editor = getenv("POWWOWPAGER")) && !(editor = getenv("PAGER"))) editor = "more"; } else { if (!(editor = getenv("POWWOWEDITOR")) && !(editor = getenv("EDITOR"))) editor = "emacs"; } if (editor[0] == '&') { waitforeditor = 0; editor++; } else waitforeditor = 1; if (waitforeditor) { tty_quit(); /* ignore SIGINT since interrupting the child would interrupt us too, if we are in the same tty group */ sig_permanent(SIGINT, SIG_IGN); sig_permanent(SIGCHLD, SIG_DFL); } switch(childpid = fork()) { /* let's get schizophrenic */ case 0: sprintf(command_str, "%s %s", editor, s->file); sprintf(buf, "TITLE=%s", s->descr); putenv(buf); /* setenv("TITLE", s->descr, 1);*/ execvp((char *)args[0], (char **)args); syserr("execve"); break; case -1: errmsg("fork"); free(s->descr); free(s->file); free(s); return; } s->pid = childpid; if (waitforeditor) { while ((i = waitpid(childpid, (int*)NULL, 0)) == -1 && errno == EINTR) ; signal_start(); /* reset SIGINT and SIGCHLD handlers */ tty_start(); if (s->fd != -1) { tty_gotoxy(0, lines - 1); tty_putc('\n'); } if (i == -1) errmsg("waitpid"); else finish_edit(s); free(s->descr); free(s->file); if (i != -1 && !edit_sess && *edit_end) { error = 0; parse_instruction(edit_end, 0, 0, 1); history_done = 0; } free(s); } else { s->next = edit_sess; edit_sess = s; } }