blob: 985f281f6dc0f1c8ddf074c6b721995dee29a8ae [file] [log] [blame]
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -07001/*
2 * SHL - PTY Helpers
3 *
4 * Copyright (c) 2011-2013 David Herrmann <dh.herrmann@gmail.com>
5 * Dedicated to the Public Domain
6 */
7
8/*
9 * PTY Helpers
10 */
11
12#include <errno.h>
13#include <fcntl.h>
14#include <limits.h>
15#include <pty.h>
16#include <signal.h>
17#include <stdbool.h>
18#include <stdint.h>
19#include <stdio.h>
20#include <stdlib.h>
21#include <string.h>
22#include <sys/epoll.h>
23#include <sys/ioctl.h>
24#include <sys/uio.h>
25#include <termios.h>
26#include <unistd.h>
27
28#include "shl_pty.h"
29
30#define SHL_PTY_BUFSIZE 16384
31
32/*
33 * Ring Buffer
34 * Our PTY helper buffers outgoing data so the caller can rely on write
35 * operations to always succeed (except for OOM). To buffer data in a PTY we
36 * use a fast ring buffer to avoid heavy re-allocations on every write.
37 *
38 * Note that this allows users to use pty-writes for small data without
39 * causing heavy allocations in the PTY layer. This is quite important for
40 * keyboard-handling or other DEC-VT emulations.
41 */
42
43struct ring {
44 char *buf;
45 size_t size;
46 size_t start;
47 size_t end;
48};
49
50#define RING_MASK(_r, _v) ((_v) & ((_r)->size - 1))
51
52/*
53 * Resize ring-buffer to size @nsize. @nsize must be a power-of-2, otherwise
54 * ring operations will behave incorrectly.
55 */
56static int ring_resize(struct ring *r, size_t nsize)
57{
58 char *buf;
59
60 buf = malloc(nsize);
61 if (!buf)
62 return -ENOMEM;
63
64 if (r->end == r->start) {
65 r->end = 0;
66 r->start = 0;
67 } else if (r->end > r->start) {
68 memcpy(buf, &r->buf[r->start], r->end - r->start);
69
70 r->end -= r->start;
71 r->start = 0;
72 } else {
73 memcpy(buf, &r->buf[r->start], r->size - r->start);
74 memcpy(&buf[r->size - r->start], r->buf, r->end);
75
76 r->end += r->size - r->start;
77 r->start = 0;
78 }
79
80 free(r->buf);
81 r->buf = buf;
82 r->size = nsize;
83
84 return 0;
85}
86
87/* Compute next higher power-of-2 of @v. Returns 4096 in case v is 0. */
88static size_t ring_pow2(size_t v)
89{
90 size_t i;
91
92 if (!v)
93 return 4096;
94
95 --v;
96
97 for (i = 1; i < 8 * sizeof (size_t); i *= 2)
98 v |= v >> i;
99
100 return ++v;
101}
102
103/*
104 * Resize ring-buffer to provide enough room for @add bytes of new data. This
105 * resizes the buffer if it is too small. It returns -ENOMEM on OOM and 0 on
106 * success.
107 */
108static int ring_grow(struct ring *r, size_t add)
109{
110 size_t len;
111
112 /*
113 * Note that "end == start" means "empty buffer". Hence, we can never
114 * fill the last byte of a buffer. That means, we must account for an
115 * additional byte here ("end == start"-byte).
116 */
117
118 if (r->end < r->start)
119 len = r->start - r->end;
120 else
121 len = r->start + r->size - r->end;
122
123 /* don't use ">=" as "end == start" would be ambigious */
124 if (len > add)
125 return 0;
126
127 /* +1 for additional "end == start" byte */
128 len = r->size + add - len + 1;
129 len = ring_pow2(len);
130
131 if (len <= r->size)
132 return -ENOMEM;
133
134 return ring_resize(r, len);
135}
136
137/*
138 * Push @len bytes from @u8 into the ring buffer. The buffer is resized if it
139 * is too small. -ENOMEM is returned on OOM, 0 on success.
140 */
141static int ring_push(struct ring *r, const char *u8, size_t len)
142{
143 int err;
144 size_t l;
145
146 err = ring_grow(r, len);
147 if (err < 0)
148 return err;
149
150 if (r->start <= r->end) {
151 l = r->size - r->end;
152 if (l > len)
153 l = len;
154
155 memcpy(&r->buf[r->end], u8, l);
156 r->end = RING_MASK(r, r->end + l);
157
158 len -= l;
159 u8 += l;
160 }
161
162 if (!len)
163 return 0;
164
165 memcpy(&r->buf[r->end], u8, len);
166 r->end = RING_MASK(r, r->end + len);
167
168 return 0;
169}
170
171/*
172 * Get data pointers for current ring-buffer data. @vec must be an array of 2
173 * iovec objects. They are filled according to the data available in the
174 * ring-buffer. 0, 1 or 2 is returned according to the number of iovec objects
175 * that were filled (0 meaning buffer is empty).
176 *
177 * Hint: "struct iovec" is defined in <sys/uio.h> and looks like this:
178 * struct iovec {
179 * void *iov_base;
180 * size_t iov_len;
181 * };
182 */
183static size_t ring_peek(struct ring *r, struct iovec *vec)
184{
185 if (r->end > r->start) {
186 vec[0].iov_base = &r->buf[r->start];
187 vec[0].iov_len = r->end - r->start;
188 return 1;
189 } else if (r->end < r->start) {
190 vec[0].iov_base = &r->buf[r->start];
191 vec[0].iov_len = r->size - r->start;
192 vec[1].iov_base = r->buf;
193 vec[1].iov_len = r->end;
194 return 2;
195 } else {
196 return 0;
197 }
198}
199
200/*
201 * Remove @len bytes from the start of the ring-buffer. Note that we protect
202 * against overflows so removing more bytes than available is safe.
203 */
204static void ring_pop(struct ring *r, size_t len)
205{
206 size_t l;
207
208 if (r->start > r->end) {
209 l = r->size - r->start;
210 if (l > len)
211 l = len;
212
213 r->start = RING_MASK(r, r->start + l);
214 len -= l;
215 }
216
217 if (!len)
218 return;
219
220 l = r->end - r->start;
221 if (l > len)
222 l = len;
223
224 r->start = RING_MASK(r, r->start + l);
225}
226
227/*
228 * PTY
229 * A PTY object represents a single PTY connection between a master and a
230 * child. The child process is fork()ed so the caller controls what program
231 * will be run.
232 *
233 * Programs like /bin/login tend to perform a vhangup() on their TTY
234 * before running the login procedure. This also causes the pty master
235 * to get a EPOLLHUP event as long as no client has the TTY opened.
236 * This means, we cannot use the TTY connection as reliable way to track
237 * the client. Instead, we _must_ rely on the PID of the client to track
238 * them.
239 * However, this has the side effect that if the client forks and the
240 * parent exits, we loose them and restart the client. But this seems to
241 * be the expected behavior so we implement it here.
242 *
243 * Unfortunately, epoll always polls for EPOLLHUP so as long as the
244 * vhangup() is ongoing, we will _always_ get EPOLLHUP and cannot sleep.
245 * This gets worse if the client closes the TTY but doesn't exit.
246 * Therefore, we the fd must be edge-triggered in the epoll-set so we
247 * only get the events once they change. This has to be taken into by the
248 * user of shl_pty. As many event-loops don't support edge-triggered
249 * behavior, you can use the shl_pty_bridge interface.
250 *
251 * Note that shl_pty does not track SIGHUP, you need to do that yourself
252 * and call shl_pty_close() once the client exited.
253 */
254
255struct shl_pty {
256 unsigned long ref;
257 int fd;
258 pid_t child;
259 char in_buf[SHL_PTY_BUFSIZE];
260 struct ring out_buf;
261
262 shl_pty_input_cb cb;
263 void *data;
264};
265
266enum shl_pty_msg {
267 SHL_PTY_FAILED,
268 SHL_PTY_SETUP,
269};
270
271static char pty_recv(int fd)
272{
273 int r;
274 char d;
275
276 do {
277 r = read(fd, &d, 1);
278 }
279 while (r < 0 && (errno == EINTR || errno == EAGAIN));
280
281 return (r <= 0) ? SHL_PTY_FAILED : d;
282}
283
284static int pty_send(int fd, char d)
285{
286 int r;
287
288 do {
289 r = write(fd, &d, 1);
290 }
291 while (r < 0 && (errno == EINTR || errno == EAGAIN));
292
293 return (r == 1) ? 0 : -EINVAL;
294}
295
296static int
297pty_setup_child(int slave, unsigned short term_width,
298 unsigned short term_height)
299{
300 struct termios attr;
301 struct winsize ws;
302
303 /* get terminal attributes */
304 if (tcgetattr(slave, &attr) < 0)
305 return -errno;
306
307 /* erase character should be normal backspace, PLEASEEE! */
308 attr.c_cc[VERASE] = 010;
309
310 /* set changed terminal attributes */
311 if (tcsetattr(slave, TCSANOW, &attr) < 0)
312 return -errno;
313
314 memset(&ws, 0, sizeof (ws));
315 ws.ws_col = term_width;
316 ws.ws_row = term_height;
317
318 if (ioctl(slave, TIOCSWINSZ, &ws) < 0)
319 return -errno;
320
321 if (dup2(slave, STDIN_FILENO) != STDIN_FILENO
322 || dup2(slave, STDOUT_FILENO) != STDOUT_FILENO
323 || dup2(slave, STDERR_FILENO) != STDERR_FILENO)
324 return -errno;
325
326 return 0;
327}
328
329static int pty_init_child(int fd)
330{
331 int r;
332 sigset_t sigset;
333 char *slave_name;
334 int slave, i;
335 pid_t pid;
336
337 /* unlockpt() requires unset signal-handlers */
338 sigemptyset(&sigset);
339 r = sigprocmask(SIG_SETMASK, &sigset, NULL);
340 if (r < 0)
341 return -errno;
342
Yunlian Jiangb9e28182018-07-08 22:49:56 -0700343 for (i = 1; i < SIGSYS; ++i)
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700344 signal(i, SIG_DFL);
345
346 r = grantpt(fd);
347 if (r < 0)
348 return -errno;
349
350 r = unlockpt(fd);
351 if (r < 0)
352 return -errno;
353
354 slave_name = ptsname(fd);
355 if (!slave_name)
356 return -errno;
357
358 /* open slave-TTY */
359 slave = open(slave_name, O_RDWR | O_CLOEXEC | O_NOCTTY);
360 if (slave < 0)
361 return -errno;
362
363 /* open session so we loose our controlling TTY */
364 pid = setsid();
365 if (pid < 0) {
366 close(slave);
367 return -errno;
368 }
369
370 /* set controlling TTY */
371 r = ioctl(slave, TIOCSCTTY, 0);
372 if (r < 0) {
373 close(slave);
374 return -errno;
375 }
376
377 return slave;
378}
379
380pid_t
381shl_pty_open(struct shl_pty ** out, shl_pty_input_cb cb, void *data,
Dominik Behrda6df412016-08-02 12:56:42 -0700382 unsigned short term_width, unsigned short term_height,
383 int pts_fd)
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700384{
385 struct shl_pty *pty;
386 pid_t pid;
387 int fd, comm[2], slave, r;
388 char d;
389
390 pty = calloc(1, sizeof (*pty));
391 if (!pty)
392 return -ENOMEM;
393
Dominik Behrda6df412016-08-02 12:56:42 -0700394 if (pts_fd >= 0)
395 fd = pts_fd;
396 else
397 fd = posix_openpt(O_RDWR | O_NOCTTY | O_CLOEXEC | O_NONBLOCK);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700398 if (fd < 0) {
399 free(pty);
400 return -errno;
401 }
402
403 r = pipe2(comm, O_CLOEXEC);
404 if (r < 0) {
405 r = -errno;
406 close(fd);
407 free(pty);
408 return r;
409 }
410
411 pid = fork();
412 if (pid < 0) {
413 /* error */
414 pid = -errno;
415 close(comm[0]);
416 close(comm[1]);
417 close(fd);
418 free(pty);
419 return pid;
420 } else if (!pid) {
421 /* child */
422 close(comm[0]);
423 free(pty);
424
425 slave = pty_init_child(fd);
426 close(fd);
427
428 if (slave < 0)
429 exit(1);
430
431 r = pty_setup_child(slave, term_width, term_height);
432 if (r < 0)
433 exit(1);
434
435 /* close slave if it's not one of the std-fds */
436 if (slave > 2)
437 close(slave);
438
439 /* wake parent */
440 pty_send(comm[1], SHL_PTY_SETUP);
441 close(comm[1]);
442
443 *out = NULL;
444 return pid;
445 }
446
447 /* parent */
448 close(comm[1]);
449
450 pty->ref = 1;
451 pty->fd = fd;
452 pty->child = pid;
453 pty->cb = cb;
454 pty->data = data;
455
456 /* wait for child setup */
457 d = pty_recv(comm[0]);
458 if (d != SHL_PTY_SETUP) {
459 close(comm[0]);
460 close(fd);
461 free(pty);
462 return -EINVAL;
463 }
464
465 close(comm[0]);
466 *out = pty;
467 return pid;
468}
469
470void shl_pty_ref(struct shl_pty *pty)
471{
472 if (!pty || !pty->ref)
473 return;
474
475 ++pty->ref;
476}
477
478void shl_pty_unref(struct shl_pty *pty)
479{
480 if (!pty || !pty->ref || --pty->ref)
481 return;
482
483 shl_pty_close(pty);
484 free(pty->out_buf.buf);
485 free(pty);
486}
487
488void shl_pty_close(struct shl_pty *pty)
489{
490 if (pty->fd < 0)
491 return;
492
493 close(pty->fd);
494 pty->fd = -1;
495}
496
Stéphane Marchesinf8807af2014-08-18 10:34:41 -0700497bool shl_pty_is_open(struct shl_pty *pty)
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700498{
499 return pty->fd >= 0;
500}
501
502int shl_pty_get_fd(struct shl_pty *pty)
503{
504 return pty->fd;
505}
506
Stéphane Marchesinf8807af2014-08-18 10:34:41 -0700507pid_t shl_pty_get_child(struct shl_pty *pty)
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700508{
509 return pty->child;
510}
511
512static void pty_write(struct shl_pty *pty)
513{
514 struct iovec vec[2];
515 size_t num;
516 ssize_t r;
517
518 num = ring_peek(&pty->out_buf, vec);
519 if (!num)
520 return;
521
522 /* ignore errors in favor of SIGCHLD; (we're edge-triggered, anyway) */
523 r = writev(pty->fd, vec, (int) num);
524 if (r >= 0)
525 ring_pop(&pty->out_buf, (size_t) r);
526}
527
528static int pty_read(struct shl_pty *pty)
529{
530 ssize_t len, num;
531
532 /* We're edge-triggered, means we need to read the whole queue. This,
533 * however, might cause us to stall if the writer is faster than we
534 * are. Therefore, we have some rather arbitrary limit on how fast
535 * we read. If we reach it, we simply return EAGAIN to the caller and
536 * let them deal with it. */
David Sodman929d0f02014-11-19 11:53:48 -0800537
538 /* Setting this as low as possible. It will slow down long
539 * file dumps, but, will respond to ctrl-c quicker, which is better
540 * for our usage model
541 */
542 num = 1;
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700543 do {
544 len = read(pty->fd, pty->in_buf, sizeof (pty->in_buf));
545 if (len > 0)
546 pty->cb(pty, pty->in_buf, len, pty->data);
547 }
548 while (len > 0 && --num);
549
550 return !num ? -EAGAIN : 0;
551}
552
553int shl_pty_dispatch(struct shl_pty *pty)
554{
555 int r;
556
557 r = pty_read(pty);
558 pty_write(pty);
559 return r;
560}
561
562int shl_pty_write(struct shl_pty *pty, const char *u8, size_t len)
563{
564 if (!shl_pty_is_open(pty))
565 return -ENODEV;
566
567 return ring_push(&pty->out_buf, u8, len);
568}
569
570int shl_pty_signal(struct shl_pty *pty, int sig)
571{
572 int r;
573
574 if (!shl_pty_is_open(pty))
575 return -ENODEV;
576
577 r = ioctl(pty->fd, TIOCSIG, sig);
578 return (r < 0) ? -errno : 0;
579}
580
581int
582shl_pty_resize(struct shl_pty *pty, unsigned short term_width,
583 unsigned short term_height)
584{
585 struct winsize ws;
586 int r;
587
588 if (!shl_pty_is_open(pty))
589 return -ENODEV;
590
591 memset(&ws, 0, sizeof (ws));
592 ws.ws_col = term_width;
593 ws.ws_row = term_height;
594
595 /*
596 * This will send SIGWINCH to the pty slave foreground process group.
597 * We will also get one, but we don't need it.
598 */
599 r = ioctl(pty->fd, TIOCSWINSZ, &ws);
600 return (r < 0) ? -errno : 0;
601}
602
603/*
604 * PTY Bridge
605 * The PTY bridge wraps multiple ptys in a single file-descriptor. It is
606 * enough for the caller to listen for read-events on the fd.
607 *
608 * This interface is provided to allow integration of PTYs into event-loops
609 * that do not support edge-triggered interfaces. There is no other reason
610 * to use this bridge.
611 */
612
613int shl_pty_bridge_new(void)
614{
615 int fd;
616
617 fd = epoll_create1(EPOLL_CLOEXEC);
618 if (fd < 0)
619 return -errno;
620
621 return fd;
622}
623
624void shl_pty_bridge_free(int bridge)
625{
626 close(bridge);
627}
628
629int shl_pty_bridge_dispatch(int bridge, int timeout)
630{
631 struct epoll_event up, ev;
632 struct shl_pty *pty;
633 int fd, r;
634
635 r = epoll_wait(bridge, &ev, 1, timeout);
636 if (r < 0) {
637 if (errno == EAGAIN || errno == EINTR)
638 return 0;
639
640 return -errno;
641 }
642
643 if (!r)
644 return 0;
645
646 pty = ev.data.ptr;
647 r = shl_pty_dispatch(pty);
648 if (r == -EAGAIN) {
649 /* EAGAIN means we couldn't dispatch data fast enough. Modify
650 * the fd in the epoll-set so we get edge-triggered events
651 * next round. */
652 memset(&up, 0, sizeof (up));
653 up.events = EPOLLIN | EPOLLOUT | EPOLLET;
654 up.data.ptr = pty;
655 fd = shl_pty_get_fd(pty);
656 epoll_ctl(bridge, EPOLL_CTL_ADD, fd, &up);
657 }
658
659 return 0;
660}
661
662int shl_pty_bridge_add(int bridge, struct shl_pty *pty)
663{
664 struct epoll_event ev;
665 int r, fd;
666
667 memset(&ev, 0, sizeof (ev));
668 ev.events = EPOLLIN | EPOLLOUT | EPOLLET;
669 ev.data.ptr = pty;
670 fd = shl_pty_get_fd(pty);
671
672 r = epoll_ctl(bridge, EPOLL_CTL_ADD, fd, &ev);
673 if (r < 0)
674 return -errno;
675
676 return 0;
677}
678
679void shl_pty_bridge_remove(int bridge, struct shl_pty *pty)
680{
681 int fd;
682
683 fd = shl_pty_get_fd(pty);
684 epoll_ctl(bridge, EPOLL_CTL_DEL, fd, NULL);
685}