AquilaOS documentation
How it works, and its code
Loading...
Searching...
No Matches
generic.c
Go to the documentation of this file.
1
16
17#include <core/system.h>
18#include <core/arch.h>
19#include <sys/sched.h>
20#include <dev/tty.h>
21#include <sys/uaccess.h>
22#include <sys/signal.h>
23#include <aquila/fcntl.h>
24
25MALLOC_DEFINE(M_TTY, "tty", "tty structure");
26MALLOC_DEFINE(M_TTY_COOK, "tty-cook", "tty cooking buffer");
27
28/* whether `c` is the special character `cc`, which is set (0 disables
29 * it: _POSIX_VDISABLE is 0) */
30#define IS_CC(tty, c, cc) ((tty)->tios.c_cc[cc] && (c) == (tty)->tios.c_cc[cc])
31
33static void tty_echo(struct tty *tty, char c)
34{
35 if ((unsigned char) c < ' ' && c != '\n' && c != '\t') {
36 char cc[] = {'^', c + '@'};
37 tty_slave_write(tty, 2, cc);
38 } else if (c == 0x7f) {
39 tty_slave_write(tty, 2, "^?");
40 } else {
41 tty_slave_write(tty, 1, &c);
42 }
43}
44
47static void tty_erase(struct tty *tty)
48{
49 if (!tty->pos)
50 return;
51
52 char c = tty->cook[--tty->pos];
53
54 if ((tty->tios.c_lflag & ECHO) && (tty->tios.c_lflag & ECHOE)) {
55 /* a control character was echoed as two characters */
56 int width = ((unsigned char) c < ' ' && c != '\t') || c == 0x7f ? 2 : 1;
57
58 while (width--)
59 tty_slave_write(tty, 3, "\b \b");
60 }
61}
62
64static void tty_deliver(struct tty *tty)
65{
66 if (tty->pos)
68
69 tty->pos = 0;
70}
71
74static int tty_signal(struct tty *tty, char c)
75{
76 int sig = IS_CC(tty, c, VINTR) ? SIGINT :
77 IS_CC(tty, c, VQUIT) ? SIGQUIT :
78 IS_CC(tty, c, VSUSP) ? SIGTSTP : 0;
79
80 if (!sig)
81 return 0;
82
83 if (!(tty->tios.c_lflag & NOFLSH))
84 tty->pos = 0;
85
86 if (tty->tios.c_lflag & ECHO) {
87 tty_echo(tty, c);
88 tty_slave_write(tty, 1, "\n");
89 }
90
91 /* the signal is only queued: this runs from the driver, maybe in an
92 * interrupt */
93 if (tty->fg)
94 signal_pgrp_send(tty->fg, sig);
95
96 return 1;
97}
98
111ssize_t tty_master_write(struct tty *tty, size_t size, void *buf)
112{
113 struct termios *tios = &tty->tios;
114 char *in = buf;
115
116 for (size_t i = 0; i < size; ++i) {
117 char c = in[i];
118
119 /* translate CR and NL */
120 if (c == '\r') {
121 if (tios->c_iflag & IGNCR)
122 continue;
123 if (tios->c_iflag & ICRNL)
124 c = '\n';
125 } else if (c == '\n' && (tios->c_iflag & INLCR)) {
126 c = '\r';
127 }
128
129 /* flow control: VSTOP stops the output, and VSTART (or any
130 * character, with IXANY) starts it again; neither is input */
131 if (tios->c_iflag & IXON) {
132 if (tty->stopped && (IS_CC(tty, c, VSTART) || (tios->c_iflag & IXANY))) {
133 tty->stopped = 0;
135
136 if (IS_CC(tty, c, VSTART))
137 continue;
138 } else if (IS_CC(tty, c, VSTART)) {
139 continue;
140 } else if (IS_CC(tty, c, VSTOP)) {
141 tty->stopped = 1;
142 continue;
143 }
144 }
145
146 if ((tios->c_lflag & ISIG) && tty_signal(tty, c))
147 continue;
148
149 if (!(tios->c_lflag & ICANON)) {
150 tty->slave_write(tty, 1, &c);
151
152 if (tios->c_lflag & ECHO)
153 tty_echo(tty, c);
154
155 continue;
156 }
157
158 if (IS_CC(tty, c, VERASE)) {
159 tty_erase(tty);
160 } else if (IS_CC(tty, c, VKILL)) {
161 while (tty->pos)
162 tty_erase(tty);
163 } else if (IS_CC(tty, c, VEOF)) {
164 /* hand over the line so far, without a newline; at the start
165 * of a line, the next read returns 0 */
166 if (!tty->pos)
167 tty->eof++;
168
170 } else if (c == '\n' || IS_CC(tty, c, VEOL)) {
171 tty->cook[tty->pos++] = c;
172
173 if ((tios->c_lflag & ECHO) || (c == '\n' && (tios->c_lflag & ECHONL)))
174 tty_echo(tty, c);
175
177 } else {
178 /* keep room for the line's newline: drop what doesn't fit */
179 if (tty->pos + 1 >= tty->size)
180 continue;
181
182 tty->cook[tty->pos++] = c;
183
184 if (tios->c_lflag & ECHO)
185 tty_echo(tty, c);
186 }
187 }
188
189 return size;
190}
191
200int tty_eof(struct tty *tty)
201{
202 /* with VMIN 0 and VTIME set, the read waits (tty_read_timeout()) */
203 if (!(tty->tios.c_lflag & ICANON))
204 return !tty->tios.c_cc[VMIN] && !tty->tios.c_cc[VTIME];
205
206 if (tty->eof) {
207 tty->eof--;
208 return 1;
209 }
210
211 return 0;
212}
213
222{
223 if ((tty->tios.c_lflag & ICANON) || tty->tios.c_cc[VMIN] || !tty->tios.c_cc[VTIME])
224 return 0;
225
226 return tty->tios.c_cc[VTIME] * 100000000ULL;
227}
228
240int tty_can_read(struct tty *tty, size_t buffered)
241{
242 return buffered || tty->eof;
243}
244
259int tty_background(struct tty *tty, int sig)
260{
261 struct proc *proc = curproc;
262
263 if (tty->session && proc->pgrp->session == tty->session &&
264 tty->fg && proc->pgrp != tty->fg) {
265 if ((proc->sigmask & (1UL << sig)) || proc->sigaction[sig].sa_handler == SIG_IGN)
266 return sig == SIGTTIN ? -EIO : 0;
267
268 /* an orphaned group isn't stopped: no one would continue it */
270 return -EIO;
271
273 return -ERESTARTSYS;
274 }
275
276 return 0;
277}
278
280static int tty_out(struct tty *tty, const char *s, size_t n)
281{
282 return tty->master_write(tty, n, (void *) s) == (ssize_t) n;
283}
284
297ssize_t tty_slave_write(struct tty *tty, size_t size, void *_buf)
298{
299 const char *buf = (const char *) _buf;
300 tcflag_t oflag = tty->tios.c_oflag;
301
302 if (!(oflag & OPOST))
303 return tty->master_write(tty, size, _buf);
304
305 size_t written;
306
307 for (written = 0; written < size; ++written) {
308 char c = buf[written];
309
310 if (c == '\n') {
311 if (oflag & ONLCR) {
312 /* CR NL; if only the CR went last time, just the NL now */
313 if (tty->nl_owed) {
314 if (!tty_out(tty, "\n", 1))
315 break;
316 } else {
317 ssize_t r = tty->master_write(tty, 2, "\r\n");
318
319 if (r != 2) {
320 tty->nl_owed = r == 1;
321 if (r == 1)
322 tty->col = 0;
323 break;
324 }
325 }
326
327 tty->nl_owed = 0;
328 tty->col = 0;
329 } else {
330 if (!tty_out(tty, "\n", 1))
331 break;
332
333 if (oflag & ONLRET)
334 tty->col = 0;
335 }
336 } else if (c == '\r') {
337 /* in the first column, there is nothing to return from */
338 if ((oflag & ONOCR) && !tty->col)
339 continue;
340
341 if (oflag & OCRNL) {
342 if (!tty_out(tty, "\n", 1))
343 break;
344
345 if (oflag & ONLRET)
346 tty->col = 0;
347 } else {
348 if (!tty_out(tty, "\r", 1))
349 break;
350
351 tty->col = 0;
352 }
353 } else {
354 if (!tty_out(tty, &c, 1))
355 break;
356
357 if (c == '\t')
358 tty->col = (tty->col | 7) + 1;
359 else if (c == '\b')
360 tty->col -= !!tty->col;
361 else if ((unsigned char) c >= ' ' && c != 0x7f)
362 tty->col++;
363 }
364 }
365
366 return written;
367}
368
369static void tty_release(struct tty *tty);
370
376static int tty_wait_start(struct tty *tty)
377{
378 int err = 0;
379
380 while (tty->stopped && !tty->gone && !err) {
382 err = waitq_sleep(&tty->start_wait);
384 }
385
386 if (tty->gone) {
387 if (!tty->start_waiters)
389
390 return err ? err : -EIO;
391 }
392
393 return err;
394}
395
408ssize_t tty_write_iobuf(struct tty *tty, struct iobuf *io, ttyio write)
409{
410 char buf[256];
411 size_t size = io->resid, done = 0;
412
413 /* a program's output to a stopped terminal waits */
414 if (write == tty_slave_write && tty->stopped) {
415 int err = tty_wait_start(tty);
416
417 if (err)
418 return err;
419 }
420
421 while (done < size) {
422 ssize_t len = iobuf_peek(io, buf, MIN(size - done, sizeof(buf)));
423
424 if (len <= 0)
425 return done ? (ssize_t) done : len;
426
427 ssize_t r = write(tty, len, buf);
428
429 if (r < 0)
430 return done ? (ssize_t) done : r;
431
432 iobuf_skip(io, r);
433 done += r;
434
435 /* the terminal took no more, or a fault stopped the copy */
436 if (r < len || io->fault)
437 break;
438 }
439
440 return done;
441}
442
455int tty_ioctl(struct tty *tty, int request, void *argp)
456{
457 int err;
458
459 switch (request) {
460 case TCGETS:
461 return copyout(argp, &tty->tios, sizeof(struct termios));
462 case TCSETS:
463 case TCSETSW: /* output is never waiting to be sent */
464 case TCSETSF: {
465 struct termios tios;
466
467 if (copyin(&tios, argp, sizeof(tios)))
468 return -EFAULT;
469
470 if ((err = tty_background(tty, SIGTTOU)))
471 return err;
472
473 if (request == TCSETSF)
474 tty_flush(tty, TCIFLUSH);
475
476 tty->tios = tios;
477
478 /* no flow control anymore: start what was stopped */
479 if (!(tios.c_iflag & IXON) && tty->stopped) {
480 tty->stopped = 0;
482 }
483 break;
484 }
485 case TCFLSH:
486 /* the argument is the value itself: TCIFLUSH, TCOFLUSH or
487 * TCIOFLUSH */
488 if ((uintptr_t) argp > TCIOFLUSH)
489 return -EINVAL;
490
491 tty_flush(tty, (uintptr_t) argp);
492 break;
493 case TCSBRK:
494 case TCXONC:
495 /* no output waits to be sent; flow control is only the
496 * typed VSTOP and VSTART (IXON), not TCXONC */
497 break;
498 case TIOCGSID:
499 if (!tty->session)
500 return -ENOTTY;
501
502 return copyout(argp, &tty->session->sid, sizeof(pid_t));
503 case TIOCGPGRP: {
504 /* without a foreground group, a value that is no process
505 * group, as POSIX says */
506 pid_t pgid = tty->fg ? tty->fg->pgid : 0x7fffffff;
507 return copyout(argp, &pgid, sizeof(pgid));
508 }
509 case TIOCSPGRP: {
510 /* only on the caller's controlling terminal, and to a group of
511 * its session */
512 pid_t pgid;
513 struct session *session = curproc->pgrp->session;
514 struct pgroup *pgrp = NULL;
515
516 if (copyin(&pgid, argp, sizeof(pgid)))
517 return -EFAULT;
518
519 if (session->ctty != tty)
520 return -ENOTTY;
521
522 if ((err = tty_background(tty, SIGTTOU)))
523 return err;
524
525 queue_for (node, session->pgps) {
526 struct pgroup *pg = node->value;
527 if (pg->pgid == pgid) {
528 pgrp = pg;
529 break;
530 }
531 }
532
533 if (!pgrp)
534 return -EPERM;
535
536 tty->fg = pgrp;
537 break;
538 }
539 case TIOCGWINSZ:
540 return copyout(argp, &tty->ws, sizeof(struct winsize));
541 case TIOCSWINSZ: {
542 struct winsize ws;
543
544 if (copyin(&ws, argp, sizeof(ws)))
545 return -EFAULT;
546
547 tty->ws = ws;
548 break;
549 }
550 case TIOCSCTTY: {
551 /* a session leader without a controlling terminal takes one
552 * that no other session has */
553 struct session *session = curproc->pgrp->session;
554
555 if (session->leader != curproc || session->ctty || tty->session)
556 return -EPERM;
557
558 session->ctty = tty;
560 tty->fg = curproc->pgrp;
561 break;
562 }
563 default:
564 /* a request that terminals don't have, as POSIX and Linux
565 * say */
566 return -ENOTTY;
567 }
568
569 return 0;
570}
571
584int tty_new(struct proc *proc, size_t buf_size, ttyio master,
585 ttyio slave, void *p, struct tty **ref)
586{
587 struct tty *tty = NULL;
588
589 tty = kmalloc(sizeof(struct tty), &M_TTY, M_ZERO);
590 if (!tty) return -ENOMEM;
591
592 if (!buf_size)
593 buf_size = TTY_BUF_SIZE;
594
595 tty->cook = kmalloc(buf_size, &M_TTY_COOK, 0);
596
597 if (!tty->cook) {
598 kfree(tty);
599 return -ENOMEM;
600 }
601
602 tty->size = buf_size;
603 tty->pos = 0;
604 tty->col = 0;
605 tty->nl_owed = 0;
606
607 /* the defaults */
608 tty->tios.c_iflag = ICRNL | IXON;
609 tty->tios.c_oflag = OPOST | ONLCR;
610 tty->tios.c_lflag = ISIG | ICANON | ECHO | ECHOE | ECHOK;
611 tty->tios.c_cc[VERASE] = 0x7f; /* DEL, what backspace sends */
612 tty->tios.c_cc[VEOF] = 0x04; /* ^D */
613 tty->tios.c_cc[VMIN] = 1;
614 tty->tios.c_cc[VINTR] = 0x03; /* ^C */
615 tty->tios.c_cc[VKILL] = 0x15; /* ^U */
616 tty->tios.c_cc[VQUIT] = 0x1C; /* ^\ */
617 tty->tios.c_cc[VSTART] = 0x11; /* ^Q */
618 tty->tios.c_cc[VSTOP] = 0x13; /* ^S */
619 tty->tios.c_cc[VSUSP] = 0x1A; /* ^Z */
620
621 tty->ws.ws_row = 24;
622 tty->ws.ws_col = 80;
623
624 /* the device's side */
625 tty->master_write = master;
626 tty->slave_write = slave;
627 tty->p = p;
628
629 if (ref)
630 *ref = tty;
631
632 return 0;
633}
634
646void tty_open(struct tty *tty, struct file *file)
647{
648 struct session *session = curproc->pgrp->session;
649 int console = file->vnode && file->vnode->rdev == DEV(5, 1);
650
651 if ((file->flags & O_NOCTTY) || console || session->leader != curproc ||
652 session->ctty || tty->session)
653 return;
654
655 session->ctty = tty;
657 tty->fg = curproc->pgrp;
658}
659
668void tty_hangup(struct tty *tty)
669{
670 if (!tty->session)
671 return;
672
673 if (tty->fg) {
674 signal_pgrp_send(tty->fg, SIGHUP);
675 signal_pgrp_send(tty->fg, SIGCONT);
676 }
677
678 tty->session->ctty = NULL;
679 tty->session = NULL;
680 tty->fg = NULL;
681}
682
691void tty_flush(struct tty *tty, int which)
692{
693 if (which == TCOFLUSH)
694 return;
695
696 tty->pos = 0;
697 tty->eof = 0;
698
699 if (tty->input_flush)
701}
702
704static void tty_release(struct tty *tty)
705{
706 kfree(tty->cook);
707 kfree(tty);
708}
709
718int tty_free(struct tty *tty)
719{
721
722 /* writers waiting for VSTART: wake them, and the last one frees it */
723 if (tty->start_waiters) {
724 tty->gone = 1;
725 tty->stopped = 0;
727 return 0;
728 }
729
731 return 0;
732}
#define DEV(major, minor)
Definition dev.h:76
void tty_hangup(struct tty *tty)
Hangs up a controlling terminal, when its session ends (its leader exited) or the terminal goes away:...
Definition generic.c:668
ssize_t tty_slave_write(struct tty *tty, size_t size, void *_buf)
Takes what programs write to the terminal, and sends it to the device through output processing (OPOS...
Definition generic.c:297
static void tty_erase(struct tty *tty)
Erases the last character of the line being edited, on the screen too with ECHOE.
Definition generic.c:47
static int tty_out(struct tty *tty, const char *s, size_t n)
Sends n bytes to the device.
Definition generic.c:280
static int tty_wait_start(struct tty *tty)
Waits while the terminal's output is stopped (IXON).
Definition generic.c:376
int tty_ioctl(struct tty *tty, int request, void *argp)
Handles a terminal's ioctl()s: its modes (TCGETS, TCSETS, ...), flushing, its session and foreground ...
Definition generic.c:455
int tty_free(struct tty *tty)
Ends a terminal: it is hung up, and freed.
Definition generic.c:718
static void tty_echo(struct tty *tty, char c)
Echoes a character, a control character as ^X.
Definition generic.c:33
ssize_t tty_write_iobuf(struct tty *tty, struct iobuf *io, ttyio write)
Writes to a terminal from an iobuf (user memory), to either side: a piece at a time through a kernel ...
Definition generic.c:408
int tty_new(struct proc *proc, size_t buf_size, ttyio master, ttyio slave, void *p, struct tty **ref)
Makes a new terminal, with the default modes (canonical, echo, signals, ICRNL, IXON,...
Definition generic.c:584
ssize_t tty_master_write(struct tty *tty, size_t size, void *buf)
Takes input from the terminal's device (the keyboard, a UART, or a pseudo-terminal's master),...
Definition generic.c:111
int tty_can_read(struct tty *tty, size_t buffered)
Says whether the terminal is readable, for select() and poll(): there is input, or an end-of-file.
Definition generic.c:240
int tty_eof(struct tty *tty)
Says whether a read that found nothing to return should return 0 at once: at an end-of-file (VEOF at ...
Definition generic.c:200
int tty_background(struct tty *tty, int sig)
Stops a background process that reads the terminal, writes it or changes it: if the current process i...
Definition generic.c:259
static void tty_deliver(struct tty *tty)
Hands the line being edited to the readers.
Definition generic.c:64
#define IS_CC(tty, c, cc)
Definition generic.c:30
static void tty_release(struct tty *tty)
Frees a terminal's memory.
Definition generic.c:704
uint64_t tty_read_timeout(struct tty *tty)
Says how long a read waits for input: VTIME tenths of a second with VMIN 0 in non-canonical mode,...
Definition generic.c:221
static int tty_signal(struct tty *tty, char c)
Handles a signal character (with ISIG): the foreground group gets the signal.
Definition generic.c:74
void tty_open(struct tty *tty, struct file *file)
Handles a terminal device being opened: it becomes the controlling terminal of a session leader that ...
Definition generic.c:646
void tty_flush(struct tty *tty, int which)
Drops a terminal's input (TCIFLUSH, TCIOFLUSH): the line being edited, what the device holds for read...
Definition generic.c:691
#define MIN(a, b)
Definition system.h:11
size_t iobuf_skip(struct iobuf *io, size_t n)
Moves an iobuf past bytes, without copying them.
Definition iobuf.c:81
ssize_t iobuf_peek(struct iobuf *io, void *dst, size_t n)
Copies bytes from the iobuf's buffer into the kernel, without moving past them.
Definition iobuf.c:154
#define MALLOC_DEFINE(type, _name, _desc)
Definition kvmem.h:30
void * kmalloc(size_t size, struct malloc_type *type, int flags)
Allocates memory for one of the kernel's structures.
Definition kvmem.c:123
#define M_ZERO
Definition kvmem.h:26
void kfree(void *)
Frees an object that kmalloc() gave.
Definition kvmem.c:146
int pgrp_orphaned(struct pgroup *pgrp, struct proc *gone)
Says whether a process group is orphaned (POSIX): none of its members has a parent in another group o...
Definition proc.c:227
#define queue_for(n, q)
Definition queue.h:37
#define curproc
Definition sched.h:13
#define ERESTARTSYS
Definition signal.h:81
#define SIG_IGN
Definition signal.h:27
int signal_pgrp_send(struct pgroup *pg, int signal)
Sends a signal to every process of a process group.
Definition signal.c:322
#define NULL
Definition stddef.h:27
__SIZE_TYPE__ uintptr_t
Definition stddef.h:12
unsigned long long int uint64_t
Definition stddef.h:23
An open file (POSIX's "open file description", fs/file.c).
Definition vfs.h:157
int flags
Definition vfs.h:164
An iobuf.
Definition iobuf.h:35
size_t resid
Definition iobuf.h:38
A process group: the processes of a job.
Definition proc.h:56
struct session * session
its session
Definition proc.h:61
pid_t pgid
the group's ID
Definition proc.h:58
A process.
Definition proc.h:75
struct aquila_sigaction sigaction[SIG_MAX+1]
its signal actions
Definition proc.h:157
struct pgroup * pgrp
its process group, and its node on the group's list
Definition proc.h:80
sigset_t sigmask
its blocked signals, a bit (1 << sig) each
Definition proc.h:151
A session: its process groups, and its controlling terminal.
Definition proc.h:26
struct queue * pgps
its process groups
Definition proc.h:31
struct tty * ctty
its controlling terminal, or NULL
Definition proc.h:37
struct proc * leader
its leader, the process that made it
Definition proc.h:34
pid_t sid
the session's ID
Definition proc.h:28
A terminal, and its line discipline's state.
Definition tty.h:25
struct pgroup * fg
the foreground process group, or NULL
Definition tty.h:63
size_t size
the size of cook
Definition tty.h:30
int gone
Definition tty.h:54
int nl_owed
Definition tty.h:43
ttyio master_write
writes to the master side: input, as if typed
Definition tty.h:74
size_t col
the output's column (for OPOST's ONOCR and ONLRET), and whether an ONLCR newline's CR was sent but no...
Definition tty.h:42
struct waitq start_wait
Definition tty.h:52
size_t pos
how much of cook is used
Definition tty.h:33
struct session * session
the session it is the controlling terminal of, or NULL
Definition tty.h:60
char * cook
the line being edited, in canonical mode
Definition tty.h:27
struct termios tios
Definition tty.h:45
struct winsize ws
Definition tty.h:46
int stopped
whether output is stopped (IXON: VSTOP came); writes wait on start_wait for VSTART.
Definition tty.h:51
void * p
the device's own data
Definition tty.h:71
int start_waiters
Definition tty.h:53
ttyio slave_write
writes to the slave side: output, for the device
Definition tty.h:77
void(*) input_flush(struct tty *tty)
discards the input that readers haven't read yet
Definition tty.h:80
int eof
how many end-of-files (VEOF at the start of a line) reads still have to return
Definition tty.h:37
ssize_t(*) ttyio(struct tty *tty, size_t size, void *buf)
Definition tty.h:22
#define TTY_BUF_SIZE
Definition tty.h:84
int pid_t
Definition types.h:8
long int ssize_t
Definition types.h:12
void waitq_wakeup(struct waitq *waitq)
Wakes up every thread sleeping on a wait queue, and every thread polling it.
Definition waitq.c:178
int waitq_sleep(struct waitq *waitq)
Puts the current thread to sleep on a wait queue until it is woken up.
Definition waitq.c:50