AquilaOS documentation
How it works, and its code
Loading...
Searching...
No Matches
pty.c
Go to the documentation of this file.
1
13
14#include <core/system.h>
15#include <sys/uaccess.h>
16#include <core/module.h>
17#include <fs/vfs.h>
18#include <fs/devpts.h>
19#include <fs/posix.h>
20#include <dev/dev.h>
21#include <dev/tty.h>
22#include <sys/sched.h>
23#include <aquila/errno.h>
24#include <ds/ringbuf.h>
25
26MALLOC_DEFINE(M_PTY, "pseudo-terminal", "pseudo-terminal structure");
27
29struct pty {
30 size_t id;
31 struct tty *tty;
32
33 struct vnode *master;
34 struct vnode *slave;
35
37 struct ringbuf *in;
38
41 struct ringbuf *out;
42
46
49};
50
51#define PTY_BUF 4096
52#define PTY_MAX 256
53
54static struct pty *ptys[PTY_MAX] = {0};
55
56/* which pseudo-terminal numbers are taken (a pseudo-terminal may not be
57 * fully set up yet) */
58static char pty_ids[PTY_MAX] = {0};
59
60struct dev ptmdev;
61struct dev ptsdev;
62
65static struct pty *pty_get(struct devid *dd)
66{
67 if (dd->major != 2 && dd->major != 136)
68 return NULL;
69
70 /* the minor is a devid_t (8 bits), so it is always below PTY_MAX */
71 return ptys[dd->minor];
72}
73
84ssize_t pty_master_write(struct tty *tty, size_t size, void *buf)
85{
86 struct pty *pty = (struct pty *) tty->p;
87 size_t written = 0;
88
89 while (written < size) {
90 written += ringbuf_write(pty->out, size - written, (char *) buf + written);
91
92 if (written != size) {
93 /* a write always completes, unless a signal comes */
94 int err = waitq_sleep(&pty->pts_read_queue);
95
96 if (err)
97 return written ? (ssize_t) written : err;
98 }
99 }
100
101 return written;
102}
103
115ssize_t pty_slave_write(struct tty *tty, size_t size, void *buf)
116{
117 struct pty *pty = (struct pty *) tty->p;
118 size_t written = 0;
119
120 while (written < size) {
121 written += ringbuf_write(pty->in, size - written, (char *) buf + written);
122
123 if (written != size) {
124 /* a write always completes, unless a signal comes */
125 int err = waitq_sleep(&pty->pts_read_queue);
126
127 if (err)
128 return written ? (ssize_t) written : err;
129 }
130 }
131
132 return written;
133}
134
136static void pty_input_flush(struct tty *tty)
137{
138 struct pty *pty = (struct pty *) tty->p;
139 char c;
140
141 while (ringbuf_read(pty->in, 1, &c))
142 ;
143}
144
145/*
146 * The master side
147 */
148
151static int ptm_new(struct pty *pty, struct vnode **ref)
152{
153 struct vnode *ptm = NULL;
154
155 ptm = kmalloc(sizeof(struct vnode), &M_VNODE, M_ZERO);
156 if (ptm == NULL) return -ENOMEM;
157
158 ptm->rdev = DEV(2, pty->id);
159 ptm->mode = S_IFCHR;
160 ptm->ref = 1; /* the descriptor /dev/ptmx was opened with */
163 ptm->p = pty;
164
165 if (ref)
166 *ref = ptm;
167
168 return 0;
169}
170
172static ssize_t ptm_read(struct devid *dd, off_t offset __unused, struct iobuf *io)
173{
174 struct pty *pty = pty_get(dd);
175 if (!pty) return -ENXIO;
176
177 ssize_t ret = ringbuf_read_iobuf(pty->out, io->resid, io);
178
180
181 return ret;
182}
183
185static ssize_t ptm_write(struct devid *dd, off_t offset __unused, struct iobuf *io)
186{
187 struct pty *pty = pty_get(dd);
188 if (!pty) return -ENXIO;
189
191}
192
195static int pty_ioctl(struct devid *dd, int request, void *argp)
196{
197 struct pty *pty = pty_get(dd);
198 if (!pty) return -ENXIO;
199
200 switch (request) {
201 case TIOCGPTN:
202 return copyout(argp, &pty->id, sizeof(int));
203 case TIOCSPTLCK: {
204 /* the lock isn't kept: the slave is always unlocked */
205 int lock;
206 return copyin(&lock, argp, sizeof(lock));
207 }
208 }
209
210 return tty_ioctl(pty->tty, request, argp);
211}
212
213/*
214 * The slave side
215 */
216
219static int pts_new(struct pty *pty, struct vnode **ref)
220{
221 char name[12];
222 memset(name, 0, 12);
223 snprintf(name, 11, "%d", pty->id);
224
225 dev_t dev = DEV(136 + pty->id / 256, pty->id % 256);
226
227 /* the opener's, and writable by the group like other terminals */
228 struct uio uio = {.uid = curproc->uid, .gid = curproc->gid};
229
230 struct vnode *pts = NULL;
231 int err = 0;
232 if ((err = vfs_vmknod(devpts_root, name, S_IFCHR | 0620, dev, &uio, &pts)))
233 return err;
234
237
238 if (ref)
239 *ref = pts;
240
241 return 0;
242}
243
253ssize_t pts_read(struct devid *dd, off_t offset __unused, struct iobuf *io)
254{
255 struct pty *pty = pty_get(dd);
256 if (!pty) return -ENXIO;
257
258 int err = tty_background(pty->tty, SIGTTIN);
259 if (err) return err;
260
261 ssize_t ret = ringbuf_read_iobuf(pty->in, io->resid, io);
262
263 /* there is room in the input now: wake up the master's writers that
264 * wait for some (pty_slave_write() sleeps on this queue) */
265 if (ret > 0)
267
268 return ret;
269}
270
280ssize_t pts_write(struct devid *dd, off_t offset __unused, struct iobuf *io)
281{
282 struct pty *pty = pty_get(dd);
283 if (!pty) return -ENXIO;
284
285 if (pty->tty->tios.c_lflag & TOSTOP) {
286 int err = tty_background(pty->tty, SIGTTOU);
287
288 if (err)
289 return err;
290 }
291
293}
294
297static int pts_eof(struct file *file)
298{
299 struct pty *pty = pty_get(&VNODE_DEV(file->vnode));
300 return !pty || tty_eof(pty->tty);
301}
302
310int pts_can_read(struct file *file, size_t size)
311{
312 struct devid *dd = &VNODE_DEV(file->vnode);
313 struct pty *pty = pty_get(dd);
314 if (!pty) return 1; /* let the read report the error */
315
316 return tty_can_read(pty->tty, ringbuf_available(pty->in));
317}
318
320static int ptm_can_read(struct file *file, size_t size)
321{
322 struct pty *pty = pty_get(&VNODE_DEV(file->vnode));
323 return !pty || ringbuf_available(pty->out);
324}
325
329{
330 struct pty *pty = pty_get(&VNODE_DEV(file->vnode));
331 return pty ? tty_read_timeout(pty->tty) : 0;
332}
333
334/*
335 * Pseudo-terminals
336 */
337
340static inline int pty_id_get(void)
341{
342 for (int id = 0; id < PTY_MAX; ++id) {
343 if (!pty_ids[id]) {
344 pty_ids[id] = 1;
345 return id;
346 }
347 }
348
349 return -ENOSPC;
350}
351
360int pty_new(struct proc *proc, struct vnode **master)
361{
362 int err = 0, id = -1;
363 struct pty *pty = NULL;
364
365 if ((id = pty_id_get()) < 0)
366 return id;
367
368 pty = kmalloc(sizeof(struct pty), &M_PTY, M_ZERO);
369 if (!pty) goto e_nomem;
370
371 pty->id = id;
372
373 if (!(pty->in = ringbuf_new(PTY_BUF))) {
374 err = -ENOMEM;
375 goto error;
376 }
377
378 if (!(pty->out = ringbuf_new(PTY_BUF))) {
379 err = -ENOMEM;
380 goto error;
381 }
382
383 if ((err = tty_new(proc, 0, pty_master_write, pty_slave_write, pty, &pty->tty)))
384 goto error;
385
386 pty->tty->dev = &ptmdev;
387
388 if ((err = ptm_new(pty, &pty->master)))
389 goto error;
390
391 if ((err = pts_new(pty, &pty->slave)))
392 goto error;
393
394 pty->tty->vnode = pty->slave;
396 ptys[pty->id] = pty;
397
398 if (master)
399 *master = pty->master;
400
401 return 0;
402
403e_nomem:
404 err = -ENOMEM;
405error:
406 if (pty) {
407 if (pty->in)
408 ringbuf_free(pty->in);
409 if (pty->out)
410 ringbuf_free(pty->out);
411 if (pty->tty)
412 tty_free(pty->tty);
413 if (pty->master)
414 kfree(pty->master);
415
416 kfree(pty);
417 }
418
419 pty_ids[id] = 0;
420
421 return err;
422}
423
426static void pty_free(struct pty *pty)
427{
428 char name[12] = {0};
429 struct uio uio = {0};
430
431 ptys[pty->id] = NULL;
432
433 /* hang up the session it controls */
434 tty_free(pty->tty);
435
436 /* the slaves still open fail with ENXIO from now on: wake up those
437 * waiting on them */
442
443 snprintf(name, 11, "%d", pty->id);
444 vfs_vunlink(devpts_root, name, &uio);
446
447 ringbuf_free(pty->in);
448 ringbuf_free(pty->out);
449 pty_ids[pty->id] = 0;
450 kfree(pty);
451}
452
460int ptmx_open(struct file *file)
461{
462 struct vnode *ptmx = file->vnode;
463 int err = pty_new(curproc, &file->vnode);
464
465 /* the file is the new master now */
466 if (!err)
467 vfs_close(ptmx);
468
469 return err;
470}
471
474static int ptm_close(struct file *file)
475{
476 struct vnode *ptm = file->vnode;
477
478 if (--ptm->ref)
479 return 0;
480
481 pty_free(ptm->p);
482 kfree(ptm);
483 return 0;
484}
485
487static int pts_open(struct file *file)
488{
489 struct pty *pty = pty_get(&VNODE_DEV(file->vnode));
490
491 if (!pty)
492 return -ENXIO;
493
494 tty_open(pty->tty, file);
495 return posix_file_open(file);
496}
497
505{
507
508 for (devid_t i = 136; i < 144; ++i)
510
511 return 0;
512}
513
514struct dev ptsdev = {
515 .name = "ptsdev",
516 .read = pts_read,
517 .write = pts_write,
518 .ioctl = pty_ioctl,
519
520 .fops = {
521 .open = pts_open,
522 .read = posix_file_read,
523 .write = posix_file_write,
524 .ioctl = posix_file_ioctl,
525
526 .can_read = pts_can_read,
527 .can_write = __vfs_can_always,
528 .eof = pts_eof,
529 .read_timeout = pts_read_timeout,
530 }
531};
532
533struct dev ptmdev = {
534 .name = "ptmdev",
535 .read = ptm_read,
536 .write = ptm_write,
537 .ioctl = pty_ioctl,
538
539 .fops = {
540 .open = ptmx_open,
541 .close = ptm_close,
542 .read = posix_file_read,
543 .write = posix_file_write,
544 .ioctl = posix_file_ioctl,
545
546 .can_read = ptm_can_read,
547 .can_write = __vfs_can_always,
548 .eof = __vfs_eof_never,
549 },
550};
551
int vfs_close(struct vnode *vnode)
Drops a reference to a vnode.
Definition close.c:18
#define DEV(major, minor)
Definition dev.h:76
#define VNODE_DEV(vnode)
Definition dev.h:79
struct vnode * devpts_root
Definition devpts.c:16
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
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
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
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
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 kdev_chrdev_register(devid_t major, struct dev *dev)
Registers a driver as the character device of a major number.
Definition kdev.c:575
#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
void * memset(void *dst, int c, size_t n)
Sets n bytes to c.
Definition libc.c:23
#define MODULE_INIT(name, i, f)
Definition module.h:10
int posix_file_open(struct file *file)
Finishes opening a file: with O_TRUNC, a regular file is truncated to 0 bytes.
Definition open.c:17
int posix_file_ioctl(struct file *file, int request, void *argp)
Passes an ioctl() request on to the file's vnode (a device's, or its filesystem's).
Definition ioctl.c:19
ssize_t posix_file_read(struct file *file, struct iobuf *io)
Reads from an open file at its offset, and moves the offset past what was read.
Definition read.c:29
ssize_t posix_file_write(struct file *file, struct iobuf *io)
Writes to an open file at its offset, and moves the offset past what was written.
Definition write.c:25
static int ptm_can_read(struct file *file, size_t size)
Returns whether the master can be read: the terminal output something.
Definition pty.c:320
struct dev ptsdev
Definition pty.c:61
int pty_init()
Registers the pseudo-terminals' devices: the masters (major 2) and the slaves (majors 136 to 143).
Definition pty.c:504
static uint64_t pts_read_timeout(struct file *file)
Returns how long a read of the slave may wait, from the terminal's mode (VTIME), or 0 for no limit.
Definition pty.c:328
static char pty_ids[PTY_MAX]
Definition pty.c:58
static struct pty * ptys[PTY_MAX]
Definition pty.c:54
static int pty_ioctl(struct devid *dd, int request, void *argp)
Handles a pseudo-terminal's ioctl()s: its number (TIOCGPTN), its lock (TIOCSPTLCK),...
Definition pty.c:195
ssize_t pty_master_write(struct tty *tty, size_t size, void *buf)
Takes the terminal's output, for the master to read.
Definition pty.c:84
static struct pty * pty_get(struct devid *dd)
Returns the pseudo-terminal of a master (major 2) or slave (major 136) device, or NULL if there is no...
Definition pty.c:65
int pty_new(struct proc *proc, struct vnode **master)
Makes a new pseudo-terminal: its rings, its terminal, its master and its slave, /dev/pts/N.
Definition pty.c:360
static ssize_t ptm_write(struct devid *dd, off_t offset, struct iobuf *io)
Writes to the terminal, as typed input.
Definition pty.c:185
static int ptm_new(struct pty *pty, struct vnode **ref)
Makes the master's vnode: an anonymous file, in no filesystem.
Definition pty.c:151
#define PTY_MAX
Definition pty.c:52
static ssize_t ptm_read(struct devid *dd, off_t offset, struct iobuf *io)
Reads the terminal's output, and wakes up the writers waiting for room.
Definition pty.c:172
static int pts_eof(struct file *file)
Returns whether a read of the slave that found no data is at the end of the file (the terminal says,...
Definition pty.c:297
ssize_t pts_read(struct devid *dd, off_t offset, struct iobuf *io)
Reads the slave's input.
Definition pty.c:253
static void pty_free(struct pty *pty)
Ends a pseudo-terminal, once its master is closed: the terminal, its /dev/pts node and its number go.
Definition pty.c:426
struct dev ptmdev
Definition pty.c:60
static int pty_id_get(void)
Takes the first free pseudo-terminal number.
Definition pty.c:340
int ptmx_open(struct file *file)
Opens /dev/ptmx: a new pseudo-terminal is made, and the file is its master instead.
Definition pty.c:460
ssize_t pts_write(struct devid *dd, off_t offset, struct iobuf *io)
Writes to the slave: the terminal's output.
Definition pty.c:280
ssize_t pty_slave_write(struct tty *tty, size_t size, void *buf)
Takes the terminal's input, after its line editing, for the slave to read.
Definition pty.c:115
int pts_can_read(struct file *file, size_t size)
Says whether a read of the slave wouldn't have to wait.
Definition pty.c:310
static int pts_open(struct file *file)
Opens the slave, which may become the process's controlling terminal.
Definition pty.c:487
static int pts_new(struct pty *pty, struct vnode **ref)
Makes the slave's node, /dev/pts/N, owned by the process that opened the master.
Definition pty.c:219
static int ptm_close(struct file *file)
Closes the master.
Definition pty.c:474
static void pty_input_flush(struct tty *tty)
Drops the input that the slave hasn't read (tcflush()).
Definition pty.c:136
#define PTY_BUF
Definition pty.c:51
#define curproc
Definition sched.h:13
int snprintf(char *s, size_t n, const char *fmt,...)
Formats a string into a buffer.
Definition snprintf.c:226
#define NULL
Definition stddef.h:27
unsigned long long int uint64_t
Definition stddef.h:23
A device driver: its operations, any of which may be NULL.
Definition dev.h:28
Which device: its kind (S_IFCHR or S_IFBLK) and numbers.
Definition dev.h:21
devid_t minor
Definition dev.h:24
devid_t major
Definition dev.h:23
An open file (POSIX's "open file description", fs/file.c).
Definition vfs.h:157
An iobuf.
Definition iobuf.h:35
size_t resid
Definition iobuf.h:38
A process.
Definition proc.h:75
A pseudo-terminal.
Definition pty.c:29
struct vnode * slave
Definition pty.c:34
size_t id
Definition pty.c:30
struct vnode * master
Definition pty.c:33
struct ringbuf * in
what the master writes, after line editing, for the slave to read
Definition pty.c:37
struct ringbuf * out
what the slave writes, after output processing, for the master to read
Definition pty.c:41
struct waitq pts_read_queue
where the slave's readers sleep, and the writers waiting for room in either ring (pty_slave_write(),...
Definition pty.c:45
struct tty * tty
Definition pty.c:31
struct waitq pts_write_queue
where the slave's writers and the master's readers sleep
Definition pty.c:48
A ring buffer.
Definition ringbuf.h:18
A terminal, and its line discipline's state.
Definition tty.h:25
struct dev * dev
the terminal's device
Definition tty.h:57
struct termios tios
Definition tty.h:45
void * p
the device's own data
Definition tty.h:71
struct vnode * vnode
the device's vnode, which /dev/tty opens in its place
Definition tty.h:66
void(*) input_flush(struct tty *tty)
discards the input that readers haven't read yet
Definition tty.h:80
Who does a filesystem operation: where its paths start, and its credentials and umask.
Definition vfs.h:60
A file in memory (a vnode): what its inode says, and the kernel's own state.
Definition vfs.h:113
struct waitq * write_queue
Definition vfs.h:137
size_t ref
how many references there are to it
Definition vfs.h:133
struct waitq * read_queue
where its readers and writers sleep
Definition vfs.h:136
void * p
the filesystem's own data
Definition vfs.h:130
mode_t mode
Definition vfs.h:118
dev_t rdev
Definition vfs.h:117
Definition waitq.h:19
uint8_t devid_t
Definition types.h:17
uint16_t dev_t
Definition types.h:18
long int ssize_t
Definition types.h:12
long int off_t
Definition types.h:10
int vfs_vunlink(struct vnode *dir, const char *fn, struct uio *uio)
Removes a name from a directory.
Definition vops.c:79
int vfs_vmknod(struct vnode *dir, const char *name, mode_t mode, dev_t dev, struct uio *uio, struct vnode **ref)
Makes a node in a directory.
Definition vops.c:22
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