2 * kmscon - Pseudo Terminal Handling
4 * Copyright (c) 2012 Ran Benita <ran234@gmail.com>
6 * Permission is hereby granted, free of charge, to any person obtaining
7 * a copy of this software and associated documentation files
8 * (the "Software"), to deal in the Software without restriction, including
9 * without limitation the rights to use, copy, modify, merge, publish,
10 * distribute, sublicense, and/or sell copies of the Software, and to
11 * permit persons to whom the Software is furnished to do so, subject to
12 * the following conditions:
14 * The above copyright notice and this permission notice shall be included
15 * in all copies or substantial portions of the Software.
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
18 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
20 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
21 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
22 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
23 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
35 #include <sys/ioctl.h>
36 #include <sys/signalfd.h>
45 #define LOG_SUBSYSTEM "pty"
47 #define KMSCON_NREAD 16384
51 struct ev_eloop *eloop;
56 struct shl_ring *msgbuf;
57 char io_buf[KMSCON_NREAD];
59 kmscon_pty_input_cb input_cb;
70 int kmscon_pty_new(struct kmscon_pty **out, kmscon_pty_input_cb input_cb,
73 struct kmscon_pty *pty;
76 if (!out || !input_cb)
79 pty = malloc(sizeof(*pty));
83 memset(pty, 0, sizeof(*pty));
86 pty->input_cb = input_cb;
89 ret = ev_eloop_new(&pty->eloop, log_llog, NULL);
93 ret = shl_ring_new(&pty->msgbuf);
97 log_debug("new pty object");
102 ev_eloop_unref(pty->eloop);
108 void kmscon_pty_ref(struct kmscon_pty *pty)
116 void kmscon_pty_unref(struct kmscon_pty *pty)
118 if (!pty || !pty->ref || --pty->ref)
121 log_debug("free pty object");
122 kmscon_pty_close(pty);
126 free(pty->colorterm);
128 shl_ring_free(pty->msgbuf);
129 ev_eloop_unref(pty->eloop);
133 int kmscon_pty_set_term(struct kmscon_pty *pty, const char *term)
149 int kmscon_pty_set_colorterm(struct kmscon_pty *pty, const char *colorterm)
153 if (!pty || !colorterm)
156 t = strdup(colorterm);
159 free(pty->colorterm);
165 int kmscon_pty_set_argv(struct kmscon_pty *pty, char **argv)
170 if (!pty || !argv || !*argv || !**argv)
173 ret = shl_dup_array(&t, argv);
182 int kmscon_pty_set_seat(struct kmscon_pty *pty, const char *seat)
198 int kmscon_pty_set_vtnr(struct kmscon_pty *pty, unsigned int vtnr)
206 ret = asprintf(&t, "%u", vtnr);
215 void kmscon_pty_set_env_reset(struct kmscon_pty *pty, bool do_reset)
220 pty->env_reset = do_reset;
223 int kmscon_pty_get_fd(struct kmscon_pty *pty)
228 return ev_eloop_get_fd(pty->eloop);
231 void kmscon_pty_dispatch(struct kmscon_pty *pty)
236 ev_eloop_dispatch(pty->eloop, 0);
239 static bool pty_is_open(struct kmscon_pty *pty)
244 static void __attribute__((noreturn))
245 exec_child(const char *term, const char *colorterm, char **argv,
246 const char *seat, const char *vtnr, bool env_reset)
252 env = malloc(sizeof(char*));
254 log_error("cannot allocate memory for environment (%d): %m",
259 memset(env, 0, sizeof(char*));
262 def_argv = (char*[]){ "/bin/login", "-p", NULL };
264 def_argv = (char*[]){ "/bin/login", NULL };
272 setenv("TERM", term, 1);
274 setenv("COLORTERM", colorterm, 1);
276 setenv("XDG_SEAT", seat, 1);
278 setenv("XDG_VTNR", vtnr, 1);
280 execve(argv[0], argv, environ);
282 log_err("failed to exec child %s: %m", argv[0]);
287 static void setup_child(int master, struct winsize *ws)
292 char slave_name[128];
296 /* The child should not inherit our signal mask. */
297 sigemptyset(&sigset);
298 ret = pthread_sigmask(SIG_SETMASK, &sigset, NULL);
300 log_warn("cannot reset blocked signals: %m");
302 for (i = 1; i < SIGUNUSED; ++i)
305 ret = grantpt(master);
307 log_err("grantpt failed: %m");
311 ret = unlockpt(master);
313 log_err("cannot unlock pty: %m");
317 ret = ptsname_r(master, slave_name, sizeof(slave_name));
319 log_err("cannot find slave name: %m");
323 /* This also loses our controlling tty. */
326 log_err("cannot start a new session: %m");
330 /* And the slave pty becomes our controlling tty. */
331 slave = open(slave_name, O_RDWR | O_CLOEXEC);
333 log_err("cannot open slave: %m");
337 /* get terminal attributes */
338 if (tcgetattr(slave, &attr) < 0) {
339 log_err("cannot get terminal attributes: %m");
343 /* erase character should be normal backspace */
344 attr.c_cc[VERASE] = 010;
346 /* set changed terminal attributes */
347 if (tcsetattr(slave, TCSANOW, &attr) < 0) {
348 log_warn("cannot set terminal attributes: %m");
353 ret = ioctl(slave, TIOCSWINSZ, ws);
355 log_warn("cannot set slave window size: %m");
358 if (dup2(slave, STDIN_FILENO) != STDIN_FILENO ||
359 dup2(slave, STDOUT_FILENO) != STDOUT_FILENO ||
360 dup2(slave, STDERR_FILENO) != STDERR_FILENO) {
361 log_err("cannot duplicate slave: %m");
378 * This is functionally equivalent to forkpty(3). We do it manually to obtain
379 * a little bit more control of the process, and as a bonus avoid linking to
380 * the libutil library in glibc.
382 static int pty_spawn(struct kmscon_pty *pty, int master,
383 unsigned short width, unsigned short height)
388 memset(&ws, 0, sizeof(ws));
395 log_err("cannot fork: %m");
398 setup_child(master, &ws);
399 exec_child(pty->term, pty->colorterm, pty->argv, pty->seat,
400 pty->vtnr, pty->env_reset);
403 log_debug("forking child %d", pid);
412 static int send_buf(struct kmscon_pty *pty)
418 while ((buf = shl_ring_peek(pty->msgbuf, &len, 0))) {
419 ret = write(pty->fd, buf, len);
421 shl_ring_drop(pty->msgbuf, ret);
425 if (ret < 0 && errno != EWOULDBLOCK) {
426 log_warn("cannot write to child process (%d): %m",
435 ev_fd_update(pty->efd, EV_READABLE | EV_ET);
439 static int read_buf(struct kmscon_pty *pty)
444 /* Use a maximum of 50 steps to avoid staying here forever.
445 * TODO: recheck where else a user might flush our queues and try to
446 * install an explicit policy. */
449 len = read(pty->fd, pty->io_buf, sizeof(pty->io_buf));
452 pty->input_cb(pty, pty->io_buf, len, pty->data);
453 } else if (len == 0) {
454 log_debug("HUP during read on pty of child %d",
457 } else if (errno != EWOULDBLOCK) {
458 log_debug("cannot read from pty of child %d (%d): %m",
462 } while (len > 0 && --num);
465 log_debug("cannot read application data fast enough");
467 /* We are edge-triggered so update the mask to get the
468 * EV_READABLE event again next round. */
469 mask = EV_READABLE | EV_ET;
470 if (!shl_ring_is_empty(pty->msgbuf))
471 mask |= EV_WRITEABLE;
472 ev_fd_update(pty->efd, mask);
478 static void pty_input(struct ev_fd *fd, int mask, void *data)
480 struct kmscon_pty *pty = data;
482 /* Programs like /bin/login tend to perform a vhangup() on their TTY
483 * before running the login procedure. This also causes the pty master
484 * to get a EV_HUP event as long as no client has the TTY opened.
485 * This means, we cannot use the TTY connection as reliable way to track
486 * the client. Instead, we _must_ rely on the PID of the client to track
488 * However, this has the side effect that if the client forks and the
489 * parent exits, we loose them and restart the client. But this seems to
490 * be the expected behavior so we implement it here.
491 * Unfortunately, epoll always polls for EPOLLHUP so as long as the
492 * vhangup() is ongoing, we will _always_ get EPOLLHUP and cannot sleep.
493 * This gets worse if the client closes the TTY but doesn't exit.
494 * Therefore, we set the fd as edge-triggered in the epoll-set so we
495 * only get the events once they change. This has to be taken into
496 * account at all places of kmscon_pty to avoid missing events. */
499 log_warn("error on pty socket of child %d", pty->child);
501 log_debug("HUP on pty of child %d", pty->child);
502 if (mask & EV_WRITEABLE)
504 if (mask & EV_READABLE)
508 static void sig_child(struct ev_eloop *eloop, struct ev_child_data *chld,
511 struct kmscon_pty *pty = data;
513 if (chld->pid != pty->child)
516 log_info("child exited: pid: %u status: %d",
517 chld->pid, chld->status);
519 pty->input_cb(pty, NULL, 0, pty->data);
522 int kmscon_pty_open(struct kmscon_pty *pty, unsigned short width,
523 unsigned short height)
531 if (pty_is_open(pty))
534 master = posix_openpt(O_RDWR | O_NOCTTY | O_CLOEXEC | O_NONBLOCK);
536 log_err("cannot open master: %m");
540 ret = ev_eloop_new_fd(pty->eloop, &pty->efd, master,
541 EV_ET | EV_READABLE, pty_input, pty);
545 ret = ev_eloop_register_child_cb(pty->eloop, sig_child, pty);
549 ret = pty_spawn(pty, master, width, height);
556 ev_eloop_unregister_child_cb(pty->eloop, sig_child, pty);
558 ev_eloop_rm_fd(pty->efd);
565 void kmscon_pty_close(struct kmscon_pty *pty)
567 if (!pty || !pty_is_open(pty))
570 ev_eloop_rm_fd(pty->efd);
572 ev_eloop_unregister_child_cb(pty->eloop, sig_child, pty);
577 int kmscon_pty_write(struct kmscon_pty *pty, const char *u8, size_t len)
581 if (!pty || !pty_is_open(pty) || !u8 || !len)
584 if (!shl_ring_is_empty(pty->msgbuf))
587 ret = write(pty->fd, u8, len);
589 if (errno != EWOULDBLOCK) {
590 log_warn("cannot write to child process");
593 } else if (ret >= len) {
595 } else if (ret > 0) {
600 ev_fd_update(pty->efd, EV_READABLE | EV_WRITEABLE | EV_ET);
603 ret = shl_ring_write(pty->msgbuf, u8, len);
605 log_warn("cannot allocate buffer; dropping output");
610 void kmscon_pty_signal(struct kmscon_pty *pty, int signum)
614 if (!pty || !pty_is_open(pty) || signum < 0)
617 ret = ioctl(pty->fd, TIOCSIG, signum);
619 log_warn("cannot send signal %d to child", signum);
623 log_debug("send signal %d to child", signum);
626 void kmscon_pty_resize(struct kmscon_pty *pty,
627 unsigned short width, unsigned short height)
632 if (!pty || !pty_is_open(pty))
635 memset(&ws, 0, sizeof(ws));
640 * This will send SIGWINCH to the pty slave foreground process group.
641 * We will also get one, but we don't need it.
643 ret = ioctl(pty->fd, TIOCSWINSZ, &ws);
645 log_warn("cannot set window size");