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;
69 int kmscon_pty_new(struct kmscon_pty **out, kmscon_pty_input_cb input_cb,
72 struct kmscon_pty *pty;
75 if (!out || !input_cb)
78 pty = malloc(sizeof(*pty));
82 memset(pty, 0, sizeof(*pty));
85 pty->input_cb = input_cb;
88 ret = ev_eloop_new(&pty->eloop, log_llog, NULL);
92 ret = shl_ring_new(&pty->msgbuf);
96 log_debug("new pty object");
101 ev_eloop_unref(pty->eloop);
107 void kmscon_pty_ref(struct kmscon_pty *pty)
115 void kmscon_pty_unref(struct kmscon_pty *pty)
117 if (!pty || !pty->ref || --pty->ref)
120 log_debug("free pty object");
121 kmscon_pty_close(pty);
124 free(pty->colorterm);
126 shl_ring_free(pty->msgbuf);
127 ev_eloop_unref(pty->eloop);
131 int kmscon_pty_set_term(struct kmscon_pty *pty, const char *term)
147 int kmscon_pty_set_colorterm(struct kmscon_pty *pty, const char *colorterm)
151 if (!pty || !colorterm)
154 t = strdup(colorterm);
157 free(pty->colorterm);
163 int kmscon_pty_set_argv(struct kmscon_pty *pty, char **argv)
168 if (!pty || !argv || !*argv || !**argv)
171 ret = shl_dup_array(&t, argv);
180 int kmscon_pty_set_seat(struct kmscon_pty *pty, const char *seat)
196 void kmscon_pty_set_env_reset(struct kmscon_pty *pty, bool do_reset)
201 pty->env_reset = do_reset;
204 int kmscon_pty_get_fd(struct kmscon_pty *pty)
209 return ev_eloop_get_fd(pty->eloop);
212 void kmscon_pty_dispatch(struct kmscon_pty *pty)
217 ev_eloop_dispatch(pty->eloop, 0);
220 static bool pty_is_open(struct kmscon_pty *pty)
225 static void __attribute__((noreturn))
226 exec_child(const char *term, const char *colorterm, char **argv,
227 const char *seat, bool env_reset)
233 env = malloc(sizeof(char*));
235 log_error("cannot allocate memory for environment (%d): %m",
240 memset(env, 0, sizeof(char*));
243 def_argv = (char*[]){ "/bin/login", "-p", NULL };
245 def_argv = (char*[]){ "/bin/login", NULL };
253 setenv("TERM", term, 1);
255 setenv("COLORTERM", colorterm, 1);
257 setenv("XDG_SEAT", seat, 1);
259 execve(argv[0], argv, environ);
261 log_err("failed to exec child %s: %m", argv[0]);
266 static void setup_child(int master, struct winsize *ws)
271 char slave_name[128];
275 /* The child should not inherit our signal mask. */
276 sigemptyset(&sigset);
277 ret = pthread_sigmask(SIG_SETMASK, &sigset, NULL);
279 log_warn("cannot reset blocked signals: %m");
281 for (i = 1; i < SIGUNUSED; ++i)
284 ret = grantpt(master);
286 log_err("grantpt failed: %m");
290 ret = unlockpt(master);
292 log_err("cannot unlock pty: %m");
296 ret = ptsname_r(master, slave_name, sizeof(slave_name));
298 log_err("cannot find slave name: %m");
302 /* This also loses our controlling tty. */
305 log_err("cannot start a new session: %m");
309 /* And the slave pty becomes our controlling tty. */
310 slave = open(slave_name, O_RDWR | O_CLOEXEC);
312 log_err("cannot open slave: %m");
316 /* get terminal attributes */
317 if (tcgetattr(slave, &attr) < 0) {
318 log_err("cannot get terminal attributes: %m");
322 /* erase character should be normal backspace */
323 attr.c_cc[VERASE] = 010;
325 /* set changed terminal attributes */
326 if (tcsetattr(slave, TCSANOW, &attr) < 0) {
327 log_warn("cannot set terminal attributes: %m");
332 ret = ioctl(slave, TIOCSWINSZ, ws);
334 log_warn("cannot set slave window size: %m");
337 if (dup2(slave, STDIN_FILENO) != STDIN_FILENO ||
338 dup2(slave, STDOUT_FILENO) != STDOUT_FILENO ||
339 dup2(slave, STDERR_FILENO) != STDERR_FILENO) {
340 log_err("cannot duplicate slave: %m");
357 * This is functionally equivalent to forkpty(3). We do it manually to obtain
358 * a little bit more control of the process, and as a bonus avoid linking to
359 * the libutil library in glibc.
361 static int pty_spawn(struct kmscon_pty *pty, int master,
362 unsigned short width, unsigned short height)
367 memset(&ws, 0, sizeof(ws));
374 log_err("cannot fork: %m");
377 setup_child(master, &ws);
378 exec_child(pty->term, pty->colorterm, pty->argv, pty->seat,
382 log_debug("forking child %d", pid);
391 static int send_buf(struct kmscon_pty *pty)
397 while ((buf = shl_ring_peek(pty->msgbuf, &len, 0))) {
398 ret = write(pty->fd, buf, len);
400 shl_ring_drop(pty->msgbuf, ret);
404 if (ret < 0 && errno != EWOULDBLOCK) {
405 log_warn("cannot write to child process (%d): %m",
414 ev_fd_update(pty->efd, EV_READABLE | EV_ET);
418 static int read_buf(struct kmscon_pty *pty)
423 /* Use a maximum of 50 steps to avoid staying here forever.
424 * TODO: recheck where else a user might flush our queues and try to
425 * install an explicit policy. */
428 len = read(pty->fd, pty->io_buf, sizeof(pty->io_buf));
431 pty->input_cb(pty, pty->io_buf, len, pty->data);
432 } else if (len == 0) {
433 log_debug("HUP during read on pty of child %d",
436 } else if (errno != EWOULDBLOCK) {
437 log_debug("cannot read from pty of child %d (%d): %m",
441 } while (len > 0 && --num);
444 log_debug("cannot read application data fast enough");
446 /* We are edge-triggered so update the mask to get the
447 * EV_READABLE event again next round. */
448 mask = EV_READABLE | EV_ET;
449 if (!shl_ring_is_empty(pty->msgbuf))
450 mask |= EV_WRITEABLE;
451 ev_fd_update(pty->efd, mask);
457 static void pty_input(struct ev_fd *fd, int mask, void *data)
459 struct kmscon_pty *pty = data;
461 /* Programs like /bin/login tend to perform a vhangup() on their TTY
462 * before running the login procedure. This also causes the pty master
463 * to get a EV_HUP event as long as no client has the TTY opened.
464 * This means, we cannot use the TTY connection as reliable way to track
465 * the client. Instead, we _must_ rely on the PID of the client to track
467 * However, this has the side effect that if the client forks and the
468 * parent exits, we loose them and restart the client. But this seems to
469 * be the expected behavior so we implement it here.
470 * Unfortunately, epoll always polls for EPOLLHUP so as long as the
471 * vhangup() is ongoing, we will _always_ get EPOLLHUP and cannot sleep.
472 * This gets worse if the client closes the TTY but doesn't exit.
473 * Therefore, we set the fd as edge-triggered in the epoll-set so we
474 * only get the events once they change. This has to be taken into
475 * account at all places of kmscon_pty to avoid missing events. */
478 log_warn("error on pty socket of child %d", pty->child);
480 log_debug("HUP on pty of child %d", pty->child);
481 if (mask & EV_WRITEABLE)
483 if (mask & EV_READABLE)
487 static void sig_child(struct ev_eloop *eloop, struct ev_child_data *chld,
490 struct kmscon_pty *pty = data;
492 if (chld->pid != pty->child)
495 log_info("child exited: pid: %u status: %d",
496 chld->pid, chld->status);
498 pty->input_cb(pty, NULL, 0, pty->data);
501 int kmscon_pty_open(struct kmscon_pty *pty, unsigned short width,
502 unsigned short height)
510 if (pty_is_open(pty))
513 master = posix_openpt(O_RDWR | O_NOCTTY | O_CLOEXEC | O_NONBLOCK);
515 log_err("cannot open master: %m");
519 ret = ev_eloop_new_fd(pty->eloop, &pty->efd, master,
520 EV_ET | EV_READABLE, pty_input, pty);
524 ret = ev_eloop_register_child_cb(pty->eloop, sig_child, pty);
528 ret = pty_spawn(pty, master, width, height);
535 ev_eloop_unregister_child_cb(pty->eloop, sig_child, pty);
537 ev_eloop_rm_fd(pty->efd);
544 void kmscon_pty_close(struct kmscon_pty *pty)
546 if (!pty || !pty_is_open(pty))
549 ev_eloop_rm_fd(pty->efd);
551 ev_eloop_unregister_child_cb(pty->eloop, sig_child, pty);
556 int kmscon_pty_write(struct kmscon_pty *pty, const char *u8, size_t len)
560 if (!pty || !pty_is_open(pty) || !u8 || !len)
563 if (!shl_ring_is_empty(pty->msgbuf))
566 ret = write(pty->fd, u8, len);
568 if (errno != EWOULDBLOCK) {
569 log_warn("cannot write to child process");
572 } else if (ret >= len) {
574 } else if (ret > 0) {
579 ev_fd_update(pty->efd, EV_READABLE | EV_WRITEABLE | EV_ET);
582 ret = shl_ring_write(pty->msgbuf, u8, len);
584 log_warn("cannot allocate buffer; dropping output");
589 void kmscon_pty_signal(struct kmscon_pty *pty, int signum)
593 if (!pty || !pty_is_open(pty) || signum < 0)
596 ret = ioctl(pty->fd, TIOCSIG, signum);
598 log_warn("cannot send signal %d to child", signum);
602 log_debug("send signal %d to child", signum);
605 void kmscon_pty_resize(struct kmscon_pty *pty,
606 unsigned short width, unsigned short height)
611 if (!pty || !pty_is_open(pty))
614 memset(&ws, 0, sizeof(ws));
619 * This will send SIGWINCH to the pty slave foreground process group.
620 * We will also get one, but we don't need it.
622 ret = ioctl(pty->fd, TIOCSWINSZ, &ws);
624 log_warn("cannot set window size");