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