uvtd: vt: implement VT_GETMODE/SETMODE ioctl state-tracking
[platform/upstream/kmscon.git] / src / pty.c
1 /*
2  * kmscon - Pseudo Terminal Handling
3  *
4  * Copyright (c) 2012 Ran Benita <ran234@gmail.com>
5  *
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:
13  *
14  * The above copyright notice and this permission notice shall be included
15  * in all copies or substantial portions of the Software.
16  *
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.
24  */
25
26 #include <errno.h>
27 #include <fcntl.h>
28 #include <inttypes.h>
29 #include <pthread.h>
30 #include <pty.h>
31 #include <signal.h>
32 #include <stdbool.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <sys/ioctl.h>
36 #include <sys/signalfd.h>
37 #include <termios.h>
38 #include <unistd.h>
39 #include "eloop.h"
40 #include "pty.h"
41 #include "shl_log.h"
42 #include "shl_misc.h"
43 #include "shl_ring.h"
44
45 #define LOG_SUBSYSTEM "pty"
46
47 #define KMSCON_NREAD 16384
48
49 struct kmscon_pty {
50         unsigned long ref;
51         struct ev_eloop *eloop;
52
53         int fd;
54         pid_t child;
55         struct ev_fd *efd;
56         struct shl_ring *msgbuf;
57         char io_buf[KMSCON_NREAD];
58
59         kmscon_pty_input_cb input_cb;
60         void *data;
61
62         char *term;
63         char *colorterm;
64         char **argv;
65         char *seat;
66         bool env_reset;
67 };
68
69 int kmscon_pty_new(struct kmscon_pty **out, kmscon_pty_input_cb input_cb,
70                    void *data)
71 {
72         struct kmscon_pty *pty;
73         int ret;
74
75         if (!out || !input_cb)
76                 return -EINVAL;
77
78         pty = malloc(sizeof(*pty));
79         if (!pty)
80                 return -ENOMEM;
81
82         memset(pty, 0, sizeof(*pty));
83         pty->fd = -1;
84         pty->ref = 1;
85         pty->input_cb = input_cb;
86         pty->data = data;
87
88         ret = ev_eloop_new(&pty->eloop, log_llog, NULL);
89         if (ret)
90                 goto err_free;
91
92         ret = shl_ring_new(&pty->msgbuf);
93         if (ret)
94                 goto err_eloop;
95
96         log_debug("new pty object");
97         *out = pty;
98         return 0;
99
100 err_eloop:
101         ev_eloop_unref(pty->eloop);
102 err_free:
103         free(pty);
104         return ret;
105 }
106
107 void kmscon_pty_ref(struct kmscon_pty *pty)
108 {
109         if (!pty)
110                 return;
111
112         pty->ref++;
113 }
114
115 void kmscon_pty_unref(struct kmscon_pty *pty)
116 {
117         if (!pty || !pty->ref || --pty->ref)
118                 return;
119
120         log_debug("free pty object");
121         kmscon_pty_close(pty);
122         free(pty->seat);
123         free(pty->argv);
124         free(pty->colorterm);
125         free(pty->term);
126         shl_ring_free(pty->msgbuf);
127         ev_eloop_unref(pty->eloop);
128         free(pty);
129 }
130
131 int kmscon_pty_set_term(struct kmscon_pty *pty, const char *term)
132 {
133         char *t;
134
135         if (!pty || !term)
136                 return -EINVAL;
137
138         t = strdup(term);
139         if (!t)
140                 return -ENOMEM;
141         free(pty->term);
142         pty->term = t;
143
144         return 0;
145 }
146
147 int kmscon_pty_set_colorterm(struct kmscon_pty *pty, const char *colorterm)
148 {
149         char *t;
150
151         if (!pty || !colorterm)
152                 return -EINVAL;
153
154         t = strdup(colorterm);
155         if (!t)
156                 return -ENOMEM;
157         free(pty->colorterm);
158         pty->colorterm = t;
159
160         return 0;
161 }
162
163 int kmscon_pty_set_argv(struct kmscon_pty *pty, char **argv)
164 {
165         char **t;
166         int ret;
167
168         if (!pty || !argv || !*argv || !**argv)
169                 return -EINVAL;
170
171         ret = shl_dup_array(&t, argv);
172         if (ret)
173                 return ret;
174
175         free(pty->argv);
176         pty->argv = t;
177         return 0;
178 }
179
180 int kmscon_pty_set_seat(struct kmscon_pty *pty, const char *seat)
181 {
182         char *t;
183
184         if (!pty || !seat)
185                 return -EINVAL;
186
187         t = strdup(seat);
188         if (!t)
189                 return -ENOMEM;
190         free(pty->seat);
191         pty->seat = t;
192
193         return 0;
194 }
195
196 void kmscon_pty_set_env_reset(struct kmscon_pty *pty, bool do_reset)
197 {
198         if (!pty)
199                 return;
200
201         pty->env_reset = do_reset;
202 }
203
204 int kmscon_pty_get_fd(struct kmscon_pty *pty)
205 {
206         if (!pty)
207                 return -EINVAL;
208
209         return ev_eloop_get_fd(pty->eloop);
210 }
211
212 void kmscon_pty_dispatch(struct kmscon_pty *pty)
213 {
214         if (!pty)
215                 return;
216
217         ev_eloop_dispatch(pty->eloop, 0);
218 }
219
220 static bool pty_is_open(struct kmscon_pty *pty)
221 {
222         return pty->fd >= 0;
223 }
224
225 static void __attribute__((noreturn))
226 exec_child(const char *term, const char *colorterm, char **argv,
227            const char *seat, bool env_reset)
228 {
229         char **env;
230         char **def_argv;
231
232         if (env_reset) {
233                 env = malloc(sizeof(char*));
234                 if (!env) {
235                         log_error("cannot allocate memory for environment (%d): %m",
236                                   errno);
237                         exit(EXIT_FAILURE);
238                 }
239
240                 memset(env, 0, sizeof(char*));
241                 environ = env;
242
243                 def_argv = (char*[]){ "/bin/login", "-p", NULL };
244         } else {
245                 def_argv = (char*[]){ "/bin/login", NULL };
246         }
247
248         if (!term)
249                 term = "vt220";
250         if (!argv)
251                 argv = def_argv;
252
253         setenv("TERM", term, 1);
254         if (colorterm)
255                 setenv("COLORTERM", colorterm, 1);
256         if (seat)
257                 setenv("XDG_SEAT", seat, 1);
258
259         execve(argv[0], argv, environ);
260
261         log_err("failed to exec child %s: %m", argv[0]);
262
263         exit(EXIT_FAILURE);
264 }
265
266 static void setup_child(int master, struct winsize *ws)
267 {
268         int ret;
269         sigset_t sigset;
270         pid_t pid;
271         char slave_name[128];
272         int slave = -1, i;
273         struct termios attr;
274
275         /* The child should not inherit our signal mask. */
276         sigemptyset(&sigset);
277         ret = pthread_sigmask(SIG_SETMASK, &sigset, NULL);
278         if (ret)
279                 log_warn("cannot reset blocked signals: %m");
280
281         for (i = 1; i < SIGUNUSED; ++i)
282                 signal(i, SIG_DFL);
283
284         ret = grantpt(master);
285         if (ret < 0) {
286                 log_err("grantpt failed: %m");
287                 goto err_out;
288         }
289
290         ret = unlockpt(master);
291         if (ret < 0) {
292                 log_err("cannot unlock pty: %m");
293                 goto err_out;
294         }
295
296         ret = ptsname_r(master, slave_name, sizeof(slave_name));
297         if (ret) {
298                 log_err("cannot find slave name: %m");
299                 goto err_out;
300         }
301
302         /* This also loses our controlling tty. */
303         pid = setsid();
304         if (pid < 0) {
305                 log_err("cannot start a new session: %m");
306                 goto err_out;
307         }
308
309         /* And the slave pty becomes our controlling tty. */
310         slave = open(slave_name, O_RDWR | O_CLOEXEC);
311         if (slave < 0) {
312                 log_err("cannot open slave: %m");
313                 goto err_out;
314         }
315
316         /* get terminal attributes */
317         if (tcgetattr(slave, &attr) < 0) {
318                 log_err("cannot get terminal attributes: %m");
319                 goto err_out;
320         }
321
322         /* erase character should be normal backspace */
323         attr.c_cc[VERASE] = 010;
324
325         /* set changed terminal attributes */
326         if (tcsetattr(slave, TCSANOW, &attr) < 0) {
327                 log_warn("cannot set terminal attributes: %m");
328                 goto err_out;
329         }
330
331         if (ws) {
332                 ret = ioctl(slave, TIOCSWINSZ, ws);
333                 if (ret)
334                         log_warn("cannot set slave window size: %m");
335         }
336
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");
341                 goto err_out;
342         }
343
344         close(master);
345         close(slave);
346         return;
347
348 err_out:
349         ret = -errno;
350         if (slave >= 0)
351                 close(slave);
352         close(master);
353         exit(EXIT_FAILURE);
354 }
355
356 /*
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.
360  */
361 static int pty_spawn(struct kmscon_pty *pty, int master,
362                         unsigned short width, unsigned short height)
363 {
364         pid_t pid;
365         struct winsize ws;
366
367         memset(&ws, 0, sizeof(ws));
368         ws.ws_col = width;
369         ws.ws_row = height;
370
371         pid = fork();
372         switch (pid) {
373         case -1:
374                 log_err("cannot fork: %m");
375                 return -errno;
376         case 0:
377                 setup_child(master, &ws);
378                 exec_child(pty->term, pty->colorterm, pty->argv, pty->seat,
379                            pty->env_reset);
380                 exit(EXIT_FAILURE);
381         default:
382                 log_debug("forking child %d", pid);
383                 pty->fd = master;
384                 pty->child = pid;
385                 break;
386         }
387
388         return 0;
389 }
390
391 static int send_buf(struct kmscon_pty *pty)
392 {
393         const char *buf;
394         size_t len;
395         int ret;
396
397         while ((buf = shl_ring_peek(pty->msgbuf, &len, 0))) {
398                 ret = write(pty->fd, buf, len);
399                 if (ret > 0) {
400                         shl_ring_drop(pty->msgbuf, ret);
401                         continue;
402                 }
403
404                 if (ret < 0 && errno != EWOULDBLOCK) {
405                         log_warn("cannot write to child process (%d): %m",
406                                  errno);
407                         return ret;
408                 }
409
410                 /* EWOULDBLOCK */
411                 return 0;
412         }
413
414         ev_fd_update(pty->efd, EV_READABLE | EV_ET);
415         return 0;
416 }
417
418 static int read_buf(struct kmscon_pty *pty)
419 {
420         ssize_t len, num;
421         int mask;
422
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. */
426         num = 50;
427         do {
428                 len = read(pty->fd, pty->io_buf, sizeof(pty->io_buf));
429                 if (len > 0) {
430                         if (pty->input_cb)
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",
434                                   pty->child);
435                         break;
436                 } else if (errno != EWOULDBLOCK) {
437                         log_debug("cannot read from pty of child %d (%d): %m",
438                                   pty->child, errno);
439                         break;
440                 }
441         } while (len > 0 && --num);
442
443         if (!num) {
444                 log_debug("cannot read application data fast enough");
445
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);
452         }
453
454         return 0;
455 }
456
457 static void pty_input(struct ev_fd *fd, int mask, void *data)
458 {
459         struct kmscon_pty *pty = data;
460
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
466          * them.
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. */
476
477         if (mask & EV_ERR)
478                 log_warn("error on pty socket of child %d", pty->child);
479         if (mask & EV_HUP)
480                 log_debug("HUP on pty of child %d", pty->child);
481         if (mask & EV_WRITEABLE)
482                 send_buf(pty);
483         if (mask & EV_READABLE)
484                 read_buf(pty);
485 }
486
487 static void sig_child(struct ev_eloop *eloop, struct ev_child_data *chld,
488                         void *data)
489 {
490         struct kmscon_pty *pty = data;
491
492         if (chld->pid != pty->child)
493                 return;
494
495         log_info("child exited: pid: %u status: %d",
496                  chld->pid, chld->status);
497
498         pty->input_cb(pty, NULL, 0, pty->data);
499 }
500
501 int kmscon_pty_open(struct kmscon_pty *pty, unsigned short width,
502                                                         unsigned short height)
503 {
504         int ret;
505         int master;
506
507         if (!pty)
508                 return -EINVAL;
509
510         if (pty_is_open(pty))
511                 return -EALREADY;
512
513         master = posix_openpt(O_RDWR | O_NOCTTY | O_CLOEXEC | O_NONBLOCK);
514         if (master < 0) {
515                 log_err("cannot open master: %m");
516                 return -errno;
517         }
518
519         ret = ev_eloop_new_fd(pty->eloop, &pty->efd, master,
520                               EV_ET | EV_READABLE, pty_input, pty);
521         if (ret)
522                 goto err_master;
523
524         ret = ev_eloop_register_child_cb(pty->eloop, sig_child, pty);
525         if (ret)
526                 goto err_fd;
527
528         ret = pty_spawn(pty, master, width, height);
529         if (ret)
530                 goto err_sig;
531
532         return 0;
533
534 err_sig:
535         ev_eloop_unregister_child_cb(pty->eloop, sig_child, pty);
536 err_fd:
537         ev_eloop_rm_fd(pty->efd);
538         pty->efd = NULL;
539 err_master:
540         close(master);
541         return ret;
542 }
543
544 void kmscon_pty_close(struct kmscon_pty *pty)
545 {
546         if (!pty || !pty_is_open(pty))
547                 return;
548
549         ev_eloop_rm_fd(pty->efd);
550         pty->efd = NULL;
551         ev_eloop_unregister_child_cb(pty->eloop, sig_child, pty);
552         close(pty->fd);
553         pty->fd = -1;
554 }
555
556 int kmscon_pty_write(struct kmscon_pty *pty, const char *u8, size_t len)
557 {
558         int ret;
559
560         if (!pty || !pty_is_open(pty) || !u8 || !len)
561                 return -EINVAL;
562
563         if (!shl_ring_is_empty(pty->msgbuf))
564                 goto buf;
565
566         ret = write(pty->fd, u8, len);
567         if (ret < 0) {
568                 if (errno != EWOULDBLOCK) {
569                         log_warn("cannot write to child process");
570                         return ret;
571                 }
572         } else if (ret >= len) {
573                 return 0;
574         } else if (ret > 0) {
575                 len -= ret;
576                 u8 = &u8[ret];
577         }
578
579         ev_fd_update(pty->efd, EV_READABLE | EV_WRITEABLE | EV_ET);
580
581 buf:
582         ret = shl_ring_write(pty->msgbuf, u8, len);
583         if (ret)
584                 log_warn("cannot allocate buffer; dropping output");
585
586         return 0;
587 }
588
589 void kmscon_pty_signal(struct kmscon_pty *pty, int signum)
590 {
591         int ret;
592
593         if (!pty || !pty_is_open(pty) || signum < 0)
594                 return;
595
596         ret = ioctl(pty->fd, TIOCSIG, signum);
597         if (ret) {
598                 log_warn("cannot send signal %d to child", signum);
599                 return;
600         }
601
602         log_debug("send signal %d to child", signum);
603 }
604
605 void kmscon_pty_resize(struct kmscon_pty *pty,
606                         unsigned short width, unsigned short height)
607 {
608         int ret;
609         struct winsize ws;
610
611         if (!pty || !pty_is_open(pty))
612                 return;
613
614         memset(&ws, 0, sizeof(ws));
615         ws.ws_col = width;
616         ws.ws_row = height;
617
618         /*
619          * This will send SIGWINCH to the pty slave foreground process group.
620          * We will also get one, but we don't need it.
621          */
622         ret = ioctl(pty->fd, TIOCSWINSZ, &ws);
623         if (ret) {
624                 log_warn("cannot set window size");
625                 return;
626         }
627 }