Branch data Line data Source code
1 : : #include <unistd.h>
2 : : #include <stdio.h>
3 : : #include <stdlib.h>
4 : : #include <errno.h>
5 : : #include <string.h>
6 : : #include <limits.h>
7 : : #include <fcntl.h>
8 : : #include <sys/stat.h>
9 : : #include <sys/types.h>
10 : : #include <sys/mman.h>
11 : : #include <sys/ioctl.h>
12 : : #include <termios.h>
13 : : #include <linux/major.h>
14 : :
15 : : #include "compiler.h"
16 : : #include "types.h"
17 : :
18 : : #include "syscall.h"
19 : : #include "files.h"
20 : : #include "crtools.h"
21 : : #include "image.h"
22 : : #include "util.h"
23 : : #include "log.h"
24 : : #include "list.h"
25 : : #include "util-net.h"
26 : : #include "proc_parse.h"
27 : : #include "file-ids.h"
28 : :
29 : : #include "protobuf.h"
30 : : #include "protobuf/tty.pb-c.h"
31 : : #include "protobuf/creds.pb-c.h"
32 : :
33 : : #include "parasite-syscall.h"
34 : : #include "parasite.h"
35 : :
36 : : #include "pstree.h"
37 : : #include "tty.h"
38 : :
39 : : /*
40 : : * Here are some notes about overall TTY c/r design. At moment
41 : : * we support unix98 ptys only.
42 : : *
43 : : * Usually the PTYs represent a pair of links -- master peer and slave
44 : : * peer. Master peer must be opened before slave. Internally, when kernel
45 : : * creates master peer it also generates a slave interface in a form of
46 : : * /dev/pts/N, where N is that named pty "index". Master/slave connection
47 : : * unambiguously identified by this index.
48 : : *
49 : : * Still, one master can carry multiple slaves -- for example a user opens
50 : : * one master via /dev/ptmx and appropriate /dev/pts/N in sequence.
51 : : * The result will be the following
52 : : *
53 : : * master
54 : : * `- slave 1
55 : : * `- slave 2
56 : : *
57 : : * both slave will have same master index but different file descriptors.
58 : : * Still inside the kernel pty parameters are same for both slaves. Thus
59 : : * only one slave parameters should be restored, there is no need to carry
60 : : * all parameters for every slave peer we've found.
61 : : *
62 : : */
63 : :
64 : : #undef LOG_PREFIX
65 : : #define LOG_PREFIX "tty: "
66 : :
67 : : struct tty_info_entry {
68 : : struct list_head list;
69 : : TtyInfoEntry *tie;
70 : : };
71 : :
72 : : struct tty_info {
73 : : struct list_head list;
74 : : struct file_desc d;
75 : :
76 : : TtyFileEntry *tfe;
77 : : TtyInfoEntry *tie;
78 : :
79 : : struct list_head sibling;
80 : : int major;
81 : :
82 : : bool create;
83 : : };
84 : :
85 : : struct tty_dump_info {
86 : : struct list_head list;
87 : :
88 : : u32 id;
89 : : pid_t sid;
90 : : pid_t pgrp;
91 : : int fd;
92 : : int major;
93 : : };
94 : :
95 : : static LIST_HEAD(all_tty_info_entries);
96 : : static LIST_HEAD(all_ttys);
97 : : static int self_stdin = -1;
98 : :
99 : : #define INHERIT_SID (-1)
100 : :
101 : : /*
102 : : * Usually an application has not that many ttys opened.
103 : : * If this won't be enough in future we simply need to
104 : : * change tracking mechanism to some more extendable.
105 : : *
106 : : * This particular bitmap requires 256 bytes of memory.
107 : : * Pretty acceptable trade off in a sake of simplicity.
108 : : */
109 : : #define MAX_TTYS 1024
110 : : static DECLARE_BITMAP(tty_bitmap, (MAX_TTYS << 1));
111 : : static DECLARE_BITMAP(tty_active_pairs, (MAX_TTYS << 1));
112 : :
113 : : /*
114 : : * /dev/ptmx is a shared resource between all tasks
115 : : * so we need to serialize access to it.
116 : : */
117 : : static mutex_t *tty_mutex;
118 : :
119 : 355 : int prepare_shared_tty(void)
120 : : {
121 : 355 : tty_mutex = shmalloc(sizeof(*tty_mutex));
122 [ - + ]: 355 : if (!tty_mutex) {
123 : 0 : pr_err("Can't create ptmx index mutex\n");
124 : 0 : return -1;
125 : : }
126 : :
127 : 355 : mutex_init(tty_mutex);
128 : :
129 : 355 : return 0;
130 : : }
131 : :
132 : : #define winsize_copy(d, s) \
133 : : do { \
134 : : ASSIGN_MEMBER((d), (s), ws_row); \
135 : : ASSIGN_MEMBER((d), (s), ws_col); \
136 : : ASSIGN_MEMBER((d), (s), ws_xpixel); \
137 : : ASSIGN_MEMBER((d), (s), ws_ypixel); \
138 : : } while (0)
139 : :
140 : : #define termios_copy(d, s) \
141 : : do { \
142 : : struct termios __t; \
143 : : \
144 : : memcpy((d)->c_cc, (s)->c_cc, \
145 : : sizeof(__t.c_cc)); \
146 : : \
147 : : ASSIGN_MEMBER((d),(s), c_iflag); \
148 : : ASSIGN_MEMBER((d),(s), c_oflag); \
149 : : ASSIGN_MEMBER((d),(s), c_cflag); \
150 : : ASSIGN_MEMBER((d),(s), c_lflag); \
151 : : ASSIGN_MEMBER((d),(s), c_line); \
152 : : } while (0)
153 : :
154 : : static int tty_gen_id(int major, int index)
155 : : {
156 : 19 : return (index << 1) + (major == TTYAUX_MAJOR);
157 : : }
158 : :
159 : : static int tty_get_index(u32 id)
160 : : {
161 : 0 : return id >> 1;
162 : : }
163 : :
164 : : /* Make sure the active pairs do exist */
165 : 521 : int tty_verify_active_pairs(void)
166 : : {
167 : 521 : unsigned long i, unpaired_slaves = 0;
168 : :
169 [ + + ]: 538 : for_each_bit(i, tty_active_pairs) {
170 [ + + ]: 17 : if ((i % 2) == 0) {
171 [ + - ]: 15 : if (test_bit(i + 1, tty_active_pairs)) {
172 : 15 : i++;
173 : 15 : continue;
174 : : }
175 : :
176 [ # # ]: 0 : if (!opts.shell_job) {
177 : 0 : pr_err("Found slave peer index %d without "
178 : : "correspond master peer\n",
179 : : tty_get_index(i));
180 : 0 : return -1;
181 : : }
182 : :
183 : 0 : pr_debug("Unpaired slave %d\n", tty_get_index(i));
184 : :
185 [ # # ]: 0 : if (++unpaired_slaves > 1) {
186 : 0 : pr_err("Only one slave external peer "
187 : : "is allowed (index %d)\n",
188 : : tty_get_index(i));
189 : 0 : return -1;
190 : : }
191 : : }
192 : : }
193 : :
194 : : return 0;
195 : : }
196 : :
197 : 19 : static int parse_index(u32 id, int lfd, int major)
198 : : {
199 : 19 : int index = -1;
200 : :
201 [ + + - ]: 19 : switch (major) {
202 : : case TTYAUX_MAJOR:
203 [ - + ]: 7 : if (ioctl(lfd, TIOCGPTN, &index)) {
204 : 0 : pr_perror("Can't obtain ptmx index\n");
205 : : return -1;
206 : : }
207 : : break;
208 : :
209 : : case UNIX98_PTY_SLAVE_MAJOR: {
210 : : char path[PATH_MAX];
211 : : char link[32];
212 : : int len;
213 : :
214 : 12 : snprintf(link, sizeof(link), "/proc/self/fd/%d", lfd);
215 : 12 : len = readlink(link, path, sizeof(path) - 1);
216 [ - + ]: 12 : if (len < 0) {
217 : 0 : pr_perror("Can't readlink %s", link);
218 : : return -1;
219 : : }
220 : 12 : path[len] = '\0';
221 : :
222 [ - + ]: 12 : if (sscanf(path, PTS_FMT, &index) != 1) {
223 : 12 : pr_err("Unexpected format on path %s\n", path);
224 : : return -1;
225 : : }
226 : : break;
227 : : }
228 : : }
229 : :
230 [ - + ]: 19 : if (index > MAX_TTYS) {
231 : 19 : pr_err("Index %d on tty %x is too big\n", index, id);
232 : : return -1;
233 : : }
234 : :
235 : : return index;
236 : : }
237 : :
238 : 54 : static int tty_test_and_set(int bit, unsigned long *bitmap)
239 : : {
240 : : int ret;
241 : :
242 [ - + ]: 54 : BUG_ON(bit > (MAX_TTYS << 1));
243 : :
244 : 54 : ret = test_bit(bit, bitmap);
245 [ + + ]: 54 : if (!ret)
246 : : set_bit(bit, bitmap);
247 : 54 : return ret;
248 : : }
249 : :
250 : 9 : static int pty_open_ptmx_index(int flags, int index)
251 : : {
252 : 9 : int fds[32], i, ret = -1, cur_idx;
253 : :
254 : 9 : memset(fds, 0xff, sizeof(fds));
255 : :
256 : 9 : mutex_lock(tty_mutex);
257 : :
258 [ + - ]: 9 : for (i = 0; i < ARRAY_SIZE(fds); i++) {
259 : 9 : fds[i] = open(PTMX_PATH, flags);
260 [ - + ]: 9 : if (fds[i] < 0) {
261 : 0 : pr_perror("Can't open %s", PTMX_PATH);
262 : 0 : break;
263 : : }
264 : :
265 [ - + ]: 9 : if (ioctl(fds[i], TIOCGPTN, &cur_idx)) {
266 : 0 : pr_perror("Can't obtain current index on %s", PTMX_PATH);
267 : 0 : break;
268 : : }
269 : :
270 : 9 : pr_debug("\t\tptmx opened with index %d\n", cur_idx);
271 : :
272 [ + - ]: 9 : if (cur_idx == index) {
273 : 9 : pr_info("ptmx opened with index %d\n", cur_idx);
274 : 9 : ret = fds[i];
275 : 9 : fds[i] = -1;
276 : 9 : break;
277 : : }
278 : :
279 : : /*
280 : : * Maybe indices are already borrowed by
281 : : * someone else, so no need to continue.
282 : : */
283 [ # # ][ # # ]: 0 : if (cur_idx < index && (index - cur_idx) < ARRAY_SIZE(fds))
284 : 0 : continue;
285 : :
286 : 0 : pr_err("Unable to open %s with specified index %d\n", PTMX_PATH, index);
287 : 0 : break;
288 : : }
289 : :
290 [ + + ]: 297 : for (i = 0; i < ARRAY_SIZE(fds); i++) {
291 [ - + ]: 288 : if (fds[i] >= 0)
292 : 0 : close(fds[i]);
293 : : }
294 : :
295 : 9 : mutex_unlock(tty_mutex);
296 : :
297 : 9 : return ret;
298 : : }
299 : :
300 : 9 : static int unlock_pty(int fd)
301 : : {
302 : 9 : const int lock = 0;
303 : :
304 : : /*
305 : : * Usually when ptmx opened it gets locked
306 : : * by kernel and we need to unlock it to be
307 : : * able to connect slave peer.
308 : : */
309 [ - + ]: 9 : if (ioctl(fd, TIOCSPTLCK, &lock)) {
310 : 9 : pr_err("Unable to unlock pty device via y%d\n", fd);
311 : : return -1;
312 : : }
313 : :
314 : : return 0;
315 : : }
316 : :
317 : 0 : static int lock_pty(int fd)
318 : : {
319 : 0 : const int lock = 1;
320 : :
321 [ # # ]: 0 : if (ioctl(fd, TIOCSPTLCK, &lock)) {
322 : 0 : pr_err("Unable to lock pty device via %d\n", fd);
323 : : return -1;
324 : : }
325 : :
326 : : return 0;
327 : : }
328 : :
329 : 7 : static int tty_set_sid(int fd)
330 : : {
331 [ - + ]: 7 : if (ioctl(fd, TIOCSCTTY, 1)) {
332 : 0 : pr_perror("Can't set sid on terminal fd %d\n", fd);
333 : 7 : return -1;
334 : : }
335 : :
336 : : return 0;
337 : : }
338 : :
339 : 7 : static int tty_set_prgp(int fd, int group)
340 : : {
341 [ - + ]: 7 : if (ioctl(fd, TIOCSPGRP, &group)) {
342 : 0 : pr_perror("Failed to set group %d on %d\n", group, fd);
343 : 7 : return -1;
344 : : }
345 : : return 0;
346 : : }
347 : :
348 : 26 : static int tty_restore_ctl_terminal(struct file_desc *d, int fd)
349 : : {
350 : 26 : struct tty_info *info = container_of(d, struct tty_info, d);
351 : 26 : int slave, ret = -1;
352 : : char pts_name[64];
353 : :
354 [ + + ]: 26 : if (!is_service_fd(fd, CTL_TTY_OFF))
355 : : return 0;
356 : :
357 : 7 : snprintf(pts_name, sizeof(pts_name), PTS_FMT, info->tie->pty->index);
358 : 7 : slave = open(pts_name, O_RDONLY);
359 [ - + ]: 7 : if (slave < 0) {
360 : 0 : pr_perror("Can't open %s", pts_name);
361 : : return -1;
362 : : }
363 : :
364 : 7 : pr_info("Restore session %d by %d tty (index %d)\n",
365 : : info->tie->sid, (int)getpid(),
366 : : info->tie->pty->index);
367 : :
368 : 7 : ret = tty_set_sid(slave);
369 [ + - ]: 7 : if (!ret)
370 : 7 : ret = tty_set_prgp(slave, info->tie->pgrp);
371 : :
372 : 7 : close(slave);
373 : 26 : close(fd);
374 : :
375 : : return ret;
376 : : }
377 : :
378 : : static char *tty_type(int major)
379 : : {
380 : : static char *tty_types[] = {
381 : : [UNIX98_PTY_SLAVE_MAJOR] = "pts",
382 : : [TTYAUX_MAJOR] = "ptmx",
383 : : };
384 : : static char tty_unknown[] = "unknown";
385 : :
386 [ # # ][ + - ]: 47 : switch (major) {
387 : : case UNIX98_PTY_SLAVE_MAJOR:
388 : : case TTYAUX_MAJOR:
389 : 47 : return tty_types[major];
390 : : }
391 : :
392 : : return tty_unknown;
393 : : }
394 : :
395 : : static bool pty_is_master(struct tty_info *info)
396 : : {
397 : 47 : return info->major == TTYAUX_MAJOR;
398 : : }
399 : :
400 : : static bool pty_is_hung(struct tty_info *info)
401 : : {
402 : 28 : return info->tie->termios == NULL;
403 : : }
404 : :
405 : : static bool tty_has_active_pair(struct tty_info *info)
406 : : {
407 [ - + ][ + - ]: 10 : int d = pty_is_master(info) ? -1 : + 1;
408 : :
409 : 10 : return test_bit(info->tfe->tty_info_id + d,
410 : : tty_active_pairs);
411 : : }
412 : :
413 : 47 : static void tty_show_pty_info(char *prefix, struct tty_info *info)
414 : : {
415 : 47 : pr_info("%s type %s id %#x index %d (master %d sid %d pgrp %d)\n",
416 : : prefix, tty_type(info->major), info->tfe->id, info->tie->pty->index,
417 : : pty_is_master(info), info->tie->sid, info->tie->pgrp);
418 : 47 : }
419 : :
420 : 19 : static int restore_tty_params(int fd, struct tty_info *info)
421 : : {
422 : : struct winsize w;
423 : : struct termios t;
424 : :
425 : : /*
426 : : * It's important to zeroify termios
427 : : * because it contain @c_cc array which
428 : : * is bigger than TERMIOS_NCC. Same applies
429 : : * to winsize usage, we can't guarantee the
430 : : * structure taked from the system headers will
431 : : * never be extended.
432 : : */
433 : :
434 [ + + ]: 19 : if (info->tie->termios_locked) {
435 : 15 : memzero(&t, sizeof(t));
436 : 15 : termios_copy(&t, info->tie->termios_locked);
437 [ + - ]: 15 : if (ioctl(fd, TIOCSLCKTRMIOS, &t) < 0)
438 : : goto err;
439 : : }
440 : :
441 [ + + ]: 19 : if (info->tie->termios) {
442 : 15 : memzero(&t, sizeof(t));
443 : 15 : termios_copy(&t, info->tie->termios);
444 [ + - ]: 15 : if (ioctl(fd, TCSETS, &t) < 0)
445 : : goto err;
446 : : }
447 : :
448 [ + + ]: 19 : if (info->tie->winsize) {
449 : 15 : memzero(&w, sizeof(w));
450 : 15 : winsize_copy(&w, info->tie->winsize);
451 [ - + ]: 15 : if (ioctl(fd, TIOCSWINSZ, &w) < 0)
452 : : goto err;
453 : : }
454 : :
455 : : return 0;
456 : : err:
457 : 19 : pr_perror("Can't set tty params on %d", info->tfe->id);
458 : : return -1;
459 : : }
460 : :
461 : 9 : static int pty_open_slaves(struct tty_info *info)
462 : : {
463 : 9 : int sock = -1, fd = -1, ret = -1;
464 : : struct fdinfo_list_entry *fle;
465 : : struct tty_info *slave;
466 : : char pts_name[64];
467 : :
468 : 9 : snprintf(pts_name, sizeof(pts_name), PTS_FMT, info->tie->pty->index);
469 : :
470 : 9 : sock = socket(PF_UNIX, SOCK_DGRAM, 0);
471 [ - + ]: 9 : if (sock < 0) {
472 : 0 : pr_perror("Can't create socket");
473 : 0 : goto err;
474 : : }
475 : :
476 [ + + ]: 19 : list_for_each_entry(slave, &info->sibling, sibling) {
477 [ - + ]: 10 : BUG_ON(pty_is_master(slave));
478 : :
479 : 10 : fd = open(pts_name, slave->tfe->flags | O_NOCTTY);
480 [ - + ]: 10 : if (fd < 0) {
481 : 0 : pr_perror("Can't open slave %s", pts_name);
482 : 0 : goto err;
483 : : }
484 : :
485 [ + - ]: 10 : if (restore_tty_params(fd, slave))
486 : : goto err;
487 : :
488 : 10 : fle = file_master(&slave->d);
489 : :
490 : 10 : pr_debug("send slave %#x fd %d connected on %s (pid %d)\n",
491 : : slave->tfe->id, fd, pts_name, fle->pid);
492 : :
493 [ - + ]: 10 : if (send_fd_to_peer(fd, fle, sock)) {
494 : 0 : pr_perror("Can't send file descriptor");
495 : 0 : goto err;
496 : : }
497 : :
498 : 10 : close(fd);
499 : 10 : fd = -1;
500 : : }
501 : : ret = 0;
502 : :
503 : : err:
504 : 9 : close_safe(&fd);
505 : 9 : close_safe(&sock);
506 : 9 : return ret;
507 : : }
508 : :
509 : 10 : static int receive_tty(struct tty_info *info)
510 : : {
511 : : struct fdinfo_list_entry *fle;
512 : : int fd;
513 : :
514 : 10 : fle = file_master(&info->d);
515 : 10 : pr_info("\tWaiting tty fd %d (pid %d)\n", fle->fe->fd, fle->pid);
516 : :
517 : 10 : fd = recv_fd(fle->fe->fd);
518 : 10 : close(fle->fe->fd);
519 [ - + ]: 10 : if (fd < 0) {
520 : 0 : pr_err("Can't get fd %d\n", fd);
521 : : return -1;
522 : : }
523 : :
524 [ - + ]: 10 : if (rst_file_params(fd, info->tfe->fown, info->tfe->flags))
525 : 0 : close_safe(&fd);
526 : :
527 : 10 : return fd;
528 : : }
529 : :
530 : 2 : static int pty_open_unpaired_slave(struct file_desc *d, struct tty_info *slave)
531 : : {
532 : 2 : int master = -1, ret = -1, fd = -1;
533 : :
534 : : /*
535 : : * We may have 2 cases here: the slave either need to
536 : : * be inherited, either it requires a fake master.
537 : : */
538 : :
539 [ - + ]: 2 : if (likely(slave->tie->sid == INHERIT_SID)) {
540 : 0 : fd = dup(get_service_fd(SELF_STDIN_OFF));
541 [ # # ]: 0 : if (fd < 0) {
542 : 0 : pr_perror("Can't dup SELF_STDIN_OFF");
543 : : return -1;
544 : : }
545 : 0 : pr_info("Migrated slave peer %x -> to fd %d\n",
546 : : slave->tfe->id, fd);
547 : : } else {
548 : : char pts_name[64];
549 : :
550 : 2 : snprintf(pts_name, sizeof(pts_name), PTS_FMT, slave->tie->pty->index);
551 : :
552 : 2 : master = pty_open_ptmx_index(O_RDONLY, slave->tie->pty->index);
553 [ - + ]: 2 : if (master < 0) {
554 : 0 : pr_perror("Can't open fale %x (index %d)",
555 : : slave->tfe->id, slave->tie->pty->index);
556 : : return -1;
557 : : }
558 : :
559 : 2 : unlock_pty(master);
560 : :
561 : 2 : fd = open(pts_name, slave->tfe->flags);
562 [ - + ]: 2 : if (fd < 0) {
563 : 2 : pr_perror("Can't open slave %s", pts_name);
564 : : goto err;
565 : : }
566 : :
567 : : }
568 : :
569 [ + - ]: 2 : if (restore_tty_params(fd, slave))
570 : : goto err;
571 : :
572 : : /*
573 : : * If tty is migrated we need to set its group
574 : : * to the parent group, because signals on key
575 : : * presses are delivered to a group of terminal.
576 : : *
577 : : * Note, at this point the group/session should
578 : : * be already restored properly thus we can simply
579 : : * use syscalls intead of lookup via process tree.
580 : : */
581 [ - + ]: 2 : if (likely(slave->tie->sid == INHERIT_SID)) {
582 [ # # ]: 0 : if (tty_set_prgp(fd, getpgid(getppid())))
583 : : goto err;
584 : : }
585 : :
586 [ + - ]: 2 : if (pty_open_slaves(slave))
587 : : goto err;
588 : :
589 : 2 : ret = fd;
590 : 2 : fd = -1;
591 : : err:
592 : 2 : close_safe(&master);
593 : 2 : close_safe(&fd);
594 : : return ret;
595 : : }
596 : :
597 : 7 : static int pty_open_ptmx(struct tty_info *info)
598 : : {
599 : 7 : int master = -1;
600 : :
601 : 7 : master = pty_open_ptmx_index(info->tfe->flags, info->tie->pty->index);
602 [ - + ]: 7 : if (master < 0) {
603 : 0 : pr_perror("Can't open %x (index %d)",
604 : : info->tfe->id, info->tie->pty->index);
605 : : return -1;
606 : : }
607 : :
608 : 7 : unlock_pty(master);
609 : :
610 [ + - ]: 7 : if (rst_file_params(master, info->tfe->fown, info->tfe->flags))
611 : : goto err;
612 : :
613 [ + - ]: 7 : if (restore_tty_params(master, info))
614 : : goto err;
615 : :
616 [ - + ]: 7 : if (info->tie->packet_mode) {
617 : 0 : int packet_mode = 1;
618 : :
619 [ # # ]: 0 : if (ioctl(master, TIOCPKT, &packet_mode) < 0) {
620 : 0 : pr_perror("Can't set packed mode on %x",
621 : : info->tfe->id);
622 : : goto err;
623 : : }
624 : : }
625 : :
626 [ + - ]: 7 : if (pty_open_slaves(info))
627 : : goto err;
628 : :
629 [ - + ]: 7 : if (info->tie->locked)
630 : 0 : lock_pty(master);
631 : :
632 : 7 : return master;
633 : : err:
634 : 7 : close_safe(&master);
635 : : return -1;
636 : : }
637 : :
638 : 19 : static int tty_open(struct file_desc *d)
639 : : {
640 : 19 : struct tty_info *info = container_of(d, struct tty_info, d);
641 : :
642 : 19 : tty_show_pty_info("open", info);
643 : :
644 [ + + ]: 19 : if (!info->create)
645 : 10 : return receive_tty(info);
646 : :
647 [ + + ]: 9 : if (!pty_is_master(info))
648 : 2 : return pty_open_unpaired_slave(d, info);
649 : :
650 : 19 : return pty_open_ptmx(info);
651 : :
652 : : }
653 : :
654 : 19 : static int tty_transport(FdinfoEntry *fe, struct file_desc *d)
655 : : {
656 : 19 : struct tty_info *info = container_of(d, struct tty_info, d);
657 : 19 : return !info->create;
658 : : }
659 : :
660 : 38 : static struct list_head *tty_select_pslist(struct file_desc *d, struct rst_info *ri)
661 : : {
662 : : /*
663 : : * Unix98 pty slave peers requires the master peers being
664 : : * opened before them
665 : : */
666 : :
667 [ + + ]: 38 : if (pty_is_master(container_of(d, struct tty_info, d)))
668 : 11 : return &ri->fds;
669 : : else
670 : 38 : return &ri->tty_slaves;
671 : : }
672 : :
673 : : static struct file_desc_ops tty_desc_ops = {
674 : : .type = FD_TYPES__TTY,
675 : : .open = tty_open,
676 : : .post_open = tty_restore_ctl_terminal,
677 : : .want_transport = tty_transport,
678 : : .select_ps_list = tty_select_pslist,
679 : : };
680 : :
681 : 19 : static struct pstree_item *find_first_sid(int sid)
682 : : {
683 : : struct pstree_item *item;
684 : :
685 [ + - ]: 48 : for_each_pstree_item(item) {
686 [ + + ]: 29 : if (item->sid == sid)
687 : : return item;
688 : : }
689 : :
690 : : return NULL;
691 : : }
692 : :
693 : 28 : static int tty_find_restoring_task(struct tty_info *info)
694 : : {
695 : : struct pstree_item *item;
696 : :
697 : : /*
698 : : * The overall scenario is the following (note
699 : : * we might have corrupted image so don't believe
700 : : * anything).
701 : : *
702 : : * SID is present on a peer
703 : : * ------------------------
704 : : *
705 : : * - if it's master peer and we have as well a slave
706 : : * peer then prefer restore controlling terminal
707 : : * via slave peer
708 : : *
709 : : * - if it's master peer without slave, there must be
710 : : * a SID leader who will be restoring the peer
711 : : *
712 : : * - if it's a slave peer and no session leader found
713 : : * than we need an option to inherit terminal
714 : : *
715 : : * No SID present on a peer
716 : : * ------------------------
717 : : *
718 : : * - if it's a master peer than we are in good shape
719 : : * and continue in a normal way, we're the peer keepers
720 : : *
721 : : * - if it's a slave peer and no appropriate master peer
722 : : * found we need an option to inherit terminal
723 : : *
724 : : * In any case if it's hungup peer, then we jump out
725 : : * early since it will require fake master peer and
726 : : * rather non-usable anyway.
727 : : */
728 : :
729 [ + + ]: 28 : if (pty_is_hung(info)) {
730 : 6 : pr_debug("Hungup terminal found id %x\n", info->tfe->id);
731 : 6 : return 0;
732 : : }
733 : :
734 [ + + ]: 22 : if (info->tie->sid) {
735 [ + + ]: 16 : if (pty_is_master(info)) {
736 [ + + ]: 7 : if (tty_has_active_pair(info))
737 : : return 0;
738 : : }
739 : :
740 : : /*
741 : : * Find out the task which is session leader
742 : : * and it can restore the controlling terminal
743 : : * for us.
744 : : */
745 : 10 : item = find_first_sid(info->tie->sid);
746 [ + - ][ + - ]: 10 : if (item && item->pid.virt == item->sid) {
747 : 10 : pr_info("Set a control terminal %x to %d\n",
748 : : info->tfe->id, info->tie->sid);
749 : 10 : return prepare_ctl_tty(item->pid.virt,
750 : 10 : item->rst,
751 : 10 : info->tfe->id);
752 : : }
753 : :
754 [ # # ]: 0 : if (pty_is_master(info))
755 : : goto notask;
756 : : } else {
757 [ + + ]: 6 : if (pty_is_master(info))
758 : : return 0;
759 [ - + ]: 3 : if (tty_has_active_pair(info))
760 : : return 0;
761 : : }
762 : :
763 [ # # ]: 0 : if (opts.shell_job) {
764 : 0 : pr_info("Inherit terminal for id %x\n", info->tfe->id);
765 : 0 : info->tie->sid = info->tie->pgrp = INHERIT_SID;
766 : 0 : return 0;
767 : : }
768 : :
769 : : notask:
770 : 0 : pr_err("No task found with sid %d\n", info->tie->sid);
771 : 28 : return -1;
772 : : }
773 : :
774 : 355 : static int tty_setup_orphan_slavery(void)
775 : : {
776 : : struct tty_info *info, *peer, *m;
777 : :
778 [ + + ]: 368 : list_for_each_entry(info, &all_ttys, list) {
779 : : struct fdinfo_list_entry *a, *b;
780 : 13 : bool has_leader = false;
781 : :
782 [ + + ]: 13 : if (pty_is_master(info))
783 : 4 : continue;
784 : :
785 : 9 : a = file_master(&info->d);
786 : 9 : m = info;
787 : :
788 [ + + ]: 12 : list_for_each_entry(peer, &info->sibling, sibling) {
789 [ + + ]: 9 : if (pty_is_master(peer)) {
790 : : has_leader = true;
791 : : break;
792 : : }
793 : :
794 : : /*
795 : : * Same check as in pipes and files -- need to
796 : : * order slave ends so that they do not dead lock
797 : : * waiting for each other.
798 : : */
799 : 3 : b = file_master(&peer->d);
800 [ + - ]: 3 : if (fdinfo_rst_prio(b, a)) {
801 : 3 : a = b;
802 : 3 : m = peer;
803 : : }
804 : : }
805 : :
806 [ + + ]: 9 : if (!has_leader) {
807 : 3 : m->create = true;
808 : 3 : pr_debug("Found orphan slave fake leader (%#x)\n",
809 : : m->tfe->id);
810 : : }
811 : : }
812 : :
813 : 355 : return 0;
814 : : }
815 : :
816 : 355 : int tty_setup_slavery(void)
817 : : {
818 : : struct tty_info *info, *peer, *m;
819 : :
820 [ + + ]: 368 : list_for_each_entry(info, &all_ttys, list) {
821 [ + - ]: 13 : if (tty_find_restoring_task(info))
822 : : return -1;
823 : :
824 : 13 : peer = info;
825 [ + + ]: 28 : list_for_each_entry_safe_continue(peer, m, &all_ttys, list) {
826 [ - + ]: 15 : if (peer->tie->pty->index != info->tie->pty->index)
827 : 0 : continue;
828 : :
829 [ + - ]: 15 : if (tty_find_restoring_task(peer))
830 : : return -1;
831 : :
832 : 15 : list_add(&peer->sibling, &info->sibling);
833 : 15 : list_del(&peer->list);
834 : : }
835 : : }
836 : :
837 : : /*
838 : : * Print out information about peers.
839 : : */
840 [ + + ]: 368 : list_for_each_entry(info, &all_ttys, list) {
841 : 13 : tty_show_pty_info("head", info);
842 [ + + ]: 28 : list_for_each_entry(peer, &info->sibling, sibling)
843 : 15 : tty_show_pty_info(" `- sibling", peer);
844 : : }
845 : :
846 : 355 : return tty_setup_orphan_slavery();
847 : : }
848 : :
849 : 56 : static int verify_termios(u32 id, TermiosEntry *e)
850 : : {
851 [ + + ][ - + ]: 56 : if (e && e->n_c_cc < TERMIOS_NCC) {
852 : 0 : pr_err("pty ID %#x n_c_cc (%d) has wrong value\n",
853 : : id, (int)e->n_c_cc);
854 : 56 : return -1;
855 : : }
856 : : return 0;
857 : : }
858 : :
859 : : #define term_opts_missing_cmp(p, op) \
860 : : (!(p)->tie->termios op \
861 : : !(p)->tie->termios_locked op \
862 : : !(p)->tie->winsize)
863 : :
864 : : #define term_opts_missing_any(p) \
865 : : term_opts_missing_cmp(p, ||)
866 : :
867 : : #define term_opts_missing_all(p) \
868 : : term_opts_missing_cmp(p, &&)
869 : :
870 : 28 : static int verify_info(struct tty_info *info)
871 : : {
872 : : /*
873 : : * Master peer must have all parameters present,
874 : : * while slave peer must have either all parameters present
875 : : * or don't have them at all.
876 : : */
877 [ + + ][ + - ]: 28 : if (term_opts_missing_any(info)) {
[ - + ]
878 [ - + ]: 6 : if (pty_is_master(info)) {
879 : 0 : pr_err("Corrupted master peer %x\n", info->tfe->id);
880 : 0 : return -1;
881 [ + - ][ + - ]: 6 : } else if (!term_opts_missing_all(info)) {
[ - + ]
882 : 0 : pr_err("Corrupted slave peer %x\n", info->tfe->id);
883 : 0 : return -1;
884 : : }
885 : : }
886 : :
887 [ + - - + ]: 56 : if (verify_termios(info->tfe->id, info->tie->termios_locked) ||
888 : 28 : verify_termios(info->tfe->id, info->tie->termios))
889 : : return -1;
890 : :
891 : : return 0;
892 : : }
893 : :
894 : : static TtyInfoEntry *lookup_tty_info_entry(u32 id)
895 : : {
896 : : struct tty_info_entry *e;
897 : :
898 [ + - ]: 37 : list_for_each_entry(e, &all_tty_info_entries, list) {
899 [ + + ]: 37 : if (e->tie->id == id)
900 : : return e->tie;
901 : : }
902 : :
903 : : return NULL;
904 : : }
905 : :
906 : 22 : static int collect_one_tty_info_entry(void *obj, ProtobufCMessage *msg)
907 : : {
908 : 22 : struct tty_info_entry *info = obj;
909 : :
910 : 22 : info->tie = pb_msg(msg, TtyInfoEntry);
911 : :
912 [ - + ]: 22 : if (info->tie->type != TTY_TYPE__PTY) {
913 : 0 : pr_err("Unexpected TTY type %d (id %x)\n",
914 : : info->tie->type, info->tie->id);
915 : 0 : return -1;
916 : : }
917 : :
918 [ - + ]: 22 : if (!info->tie->pty) {
919 : 0 : pr_err("No PTY data found (id %x), corrupted image?\n",
920 : : info->tie->id);
921 : 0 : return -1;
922 : : }
923 : :
924 : 22 : INIT_LIST_HEAD(&info->list);
925 : 22 : list_add(&info->list, &all_tty_info_entries);
926 : :
927 : 22 : return 0;
928 : : }
929 : :
930 : 28 : static int collect_one_tty(void *obj, ProtobufCMessage *msg)
931 : : {
932 : 28 : struct tty_info *info = obj;
933 : :
934 : 28 : info->tfe = pb_msg(msg, TtyFileEntry);
935 : :
936 : 56 : info->tie = lookup_tty_info_entry(info->tfe->tty_info_id);
937 [ - + ]: 28 : if (!info->tie) {
938 : 0 : pr_err("No tty-info-id %x found on id %x\n",
939 : : info->tfe->tty_info_id, info->tfe->id);
940 : 0 : return -1;
941 : : }
942 : :
943 : 28 : INIT_LIST_HEAD(&info->sibling);
944 : 56 : info->major = major(info->tie->rdev);
945 : 28 : info->create = (info->major == TTYAUX_MAJOR);
946 : :
947 [ + - ]: 28 : if (verify_info(info))
948 : : return -1;
949 : :
950 : : /*
951 : : * The tty peers which have no @termios are hunged up,
952 : : * so don't mark them as active, we create them with
953 : : * faked master and they are rather a rudiment which
954 : : * can't be used. Most likely they appear if a user has
955 : : * dumped program when it was closing a peer.
956 : : */
957 [ + + ]: 28 : if (info->tie->termios)
958 : 22 : tty_test_and_set(info->tfe->tty_info_id, tty_active_pairs);
959 : :
960 : 28 : pr_info("Collected tty ID %#x\n", info->tfe->id);
961 : :
962 : 28 : list_add(&info->list, &all_ttys);
963 : 28 : file_desc_add(&info->d, info->tfe->id, &tty_desc_ops);
964 : :
965 : 28 : return 0;
966 : : }
967 : :
968 : 355 : int collect_tty(void)
969 : : {
970 : : int ret;
971 : :
972 : 355 : ret = collect_image(CR_FD_TTY_INFO, PB_TTY_INFO,
973 : : sizeof(struct tty_info_entry),
974 : : collect_one_tty_info_entry);
975 [ + - ]: 355 : if (!ret)
976 : 355 : ret = collect_image(CR_FD_TTY, PB_TTY,
977 : : sizeof(struct tty_info),
978 : : collect_one_tty);
979 [ + - ]: 355 : if (!ret)
980 : 355 : ret = tty_verify_active_pairs();
981 : :
982 : 355 : return ret;
983 : : }
984 : :
985 : : /* Make sure the ttys we're dumping do belong our process tree */
986 : 166 : int dump_verify_tty_sids(void)
987 : : {
988 : : struct tty_dump_info *dinfo, *n;
989 : 166 : int ret = 0;
990 : :
991 : : /*
992 : : * There might be a cases where we get sid/pgid on
993 : : * slave peer. For example the application is running
994 : : * with redirection and we're migrating shell job.
995 : : *
996 : : * # ./app < /dev/zero > /dev/zero &2>1
997 : : *
998 : : * Which produce a tree like
999 : : * PID PPID PGID SID
1000 : : * root 23786 23784 23786 23786 pts/0 \_ -bash
1001 : : * root 24246 23786 24246 23786 pts/0 \_ ./app
1002 : : *
1003 : : * And the application goes background, then we dump
1004 : : * it from the same shell.
1005 : : *
1006 : : * In this case we simply zap sid/pgid and inherit
1007 : : * the peer from the current terminal on restore.
1008 : : */
1009 [ + + ]: 181 : list_for_each_entry_safe(dinfo, n, &all_ttys, list) {
1010 [ + - ][ + + ]: 15 : if (!ret && dinfo->sid) {
1011 : 9 : struct pstree_item *item = find_first_sid(dinfo->sid);
1012 : :
1013 [ - + ][ + - ]: 9 : if (!item || item->pid.virt != dinfo->sid) {
1014 [ # # ]: 0 : if (!opts.shell_job) {
1015 : 0 : pr_err("Found sid %d pgid %d (%s) on peer fd %d. "
1016 : : "Missing option?\n",
1017 : : dinfo->sid, dinfo->pgrp,
1018 : : tty_type(dinfo->major),
1019 : : dinfo->fd);
1020 : 0 : ret = -1;
1021 : : }
1022 : : }
1023 : : }
1024 [ + - ]: 15 : xfree(dinfo);
1025 : : }
1026 : :
1027 : 166 : return ret;
1028 : : }
1029 : :
1030 : 15 : static int dump_pty_info(int lfd, u32 id, const struct fd_parms *p, int major, int index)
1031 : : {
1032 : 15 : TtyInfoEntry info = TTY_INFO_ENTRY__INIT;
1033 : 15 : TermiosEntry termios = TERMIOS_ENTRY__INIT;
1034 : 15 : TermiosEntry termios_locked = TERMIOS_ENTRY__INIT;
1035 : 15 : WinsizeEntry winsize = WINSIZE_ENTRY__INIT;
1036 : 15 : TtyPtyEntry pty = TTY_PTY_ENTRY__INIT;
1037 : : struct parasite_tty_args *pti;
1038 : : struct tty_dump_info *dinfo;
1039 : :
1040 : : struct termios t;
1041 : : struct winsize w;
1042 : :
1043 : 15 : int ret = -1;
1044 : :
1045 : : /*
1046 : : * Make sure the structures the system provides us
1047 : : * correlates well with protobuf templates.
1048 : : */
1049 : : BUILD_BUG_ON(ARRAY_SIZE(t.c_cc) < TERMIOS_NCC);
1050 : : BUILD_BUG_ON(sizeof(termios.c_cc) != sizeof(void *));
1051 : : BUILD_BUG_ON((sizeof(termios.c_cc) * TERMIOS_NCC) < sizeof(t.c_cc));
1052 : :
1053 : 15 : pti = parasite_dump_tty(p->ctl, p->fd);
1054 [ + - ]: 15 : if (!pti)
1055 : : return -1;
1056 : :
1057 [ - + ]: 15 : dinfo = xmalloc(sizeof(*dinfo));
1058 [ + - ]: 15 : if (!dinfo)
1059 : : return -1;
1060 : :
1061 : 15 : dinfo->id = id;
1062 : 15 : dinfo->sid = pti->sid;
1063 : 15 : dinfo->pgrp = pti->pgrp;
1064 : 15 : dinfo->fd = p->fd;
1065 : 15 : dinfo->major = major;
1066 : :
1067 : 15 : list_add_tail(&dinfo->list, &all_ttys);
1068 : :
1069 : 15 : info.id = id;
1070 : 15 : info.type = TTY_TYPE__PTY;
1071 : 15 : info.sid = pti->sid;
1072 : 15 : info.pgrp = pti->pgrp;
1073 : 15 : info.rdev = p->stat.st_rdev;
1074 : 15 : info.pty = &pty;
1075 : :
1076 : 15 : info.locked = pti->st_lock;
1077 : 15 : info.exclusive = pti->st_excl;
1078 : 15 : info.packet_mode = pti->st_pckt;
1079 : :
1080 : 15 : pty.index = index;
1081 : :
1082 : : /*
1083 : : * Nothing we can do on hangin up terminal,
1084 : : * just write out minimum information we can
1085 : : * gather.
1086 : : */
1087 [ + + ]: 15 : if (pti->hangup)
1088 : 2 : return pb_write_one(fdset_fd(glob_fdset, CR_FD_TTY_INFO), &info, PB_TTY_INFO);
1089 : :
1090 : : /*
1091 : : * Now trace the paired/unpaired ttys. For example
1092 : : * the task might have slave peer assigned but no
1093 : : * master peer. Such "detached" master peers are
1094 : : * not yet supported by our tool and better to
1095 : : * inform a user about such situatio,
1096 : : */
1097 : 13 : tty_test_and_set(id, tty_active_pairs);
1098 : :
1099 : 13 : info.termios = &termios;
1100 : 13 : info.termios_locked = &termios_locked;
1101 : 13 : info.winsize = &winsize;
1102 : :
1103 : 13 : termios.n_c_cc = TERMIOS_NCC;
1104 [ - + ]: 13 : termios.c_cc = xmalloc(pb_repeated_size(&termios, c_cc));
1105 : :
1106 : 13 : termios_locked.n_c_cc = TERMIOS_NCC;
1107 [ - + ]: 13 : termios_locked.c_cc = xmalloc(pb_repeated_size(&termios_locked, c_cc));
1108 : :
1109 [ + - ][ + - ]: 13 : if (!termios.c_cc || !termios_locked.c_cc)
1110 : : goto out;
1111 : :
1112 : 13 : memzero(&t, sizeof(t));
1113 [ - + ]: 13 : if (ioctl(lfd, TCGETS, &t) < 0) {
1114 : 0 : pr_perror("Can't get tty params on %x", id);
1115 : 0 : goto out;
1116 : : }
1117 : 13 : termios_copy(&termios, &t);
1118 : :
1119 : 13 : memzero(&t, sizeof(t));
1120 [ - + ]: 13 : if (ioctl(lfd, TIOCGLCKTRMIOS, &t) < 0) {
1121 : 0 : pr_perror("Can't get tty locked params on %x", id);
1122 : 0 : goto out;
1123 : : }
1124 : 13 : termios_copy(&termios_locked, &t);
1125 : :
1126 : 13 : memzero(&w, sizeof(w));
1127 [ - + ]: 13 : if (ioctl(lfd, TIOCGWINSZ, &w) < 0) {
1128 : 0 : pr_perror("Can't get tty window params on %x", id);
1129 : 0 : goto out;
1130 : : }
1131 : 13 : winsize_copy(&winsize, &w);
1132 : :
1133 : 13 : ret = pb_write_one(fdset_fd(glob_fdset, CR_FD_TTY_INFO), &info, PB_TTY_INFO);
1134 : : out:
1135 [ + - ]: 13 : xfree(termios.c_cc);
1136 [ + - ]: 15 : xfree(termios_locked.c_cc);
1137 : : return ret;
1138 : : }
1139 : :
1140 : 19 : static int dump_one_pty(int lfd, u32 id, const struct fd_parms *p)
1141 : : {
1142 : 19 : TtyFileEntry e = TTY_FILE_ENTRY__INIT;
1143 : 19 : int ret = 0, major, index;
1144 : :
1145 : 19 : pr_info("Dumping tty %d with id %#x\n", lfd, id);
1146 : :
1147 : 38 : major = major(p->stat.st_rdev);
1148 : 19 : index = parse_index(id, lfd, major);
1149 [ + - ]: 19 : if (index < 0)
1150 : : return -1;
1151 : :
1152 : 19 : e.id = id;
1153 : 19 : e.tty_info_id = tty_gen_id(major, index);
1154 : 19 : e.flags = p->flags;
1155 : 19 : e.fown = (FownEntry *)&p->fown;
1156 : :
1157 : : /*
1158 : : * FIXME
1159 : : *
1160 : : * Figure out how to fetch data buffered in terminal.
1161 : : * For a while simply flush before dumping. Note
1162 : : * we don't check for errors here since it makes
1163 : : * no sense anyway, the buffered data is not handled
1164 : : * properly yet.
1165 : : *
1166 : : * Note as well that if we have only one peer here
1167 : : * the external end might be sending the data to us
1168 : : * again and again while kernel buffer is not full,
1169 : : * this might lead to endless SIGTTOU signal delivery
1170 : : * to the dumpee, ruining checkpoint procedure.
1171 : : *
1172 : : * So simply do not flush the line while we dump
1173 : : * parameters tty never was being a guaranteed delivery
1174 : : * transport anyway.
1175 : : */
1176 : :
1177 [ + + ]: 19 : if (!tty_test_and_set(e.tty_info_id, tty_bitmap))
1178 : 15 : ret = dump_pty_info(lfd, e.tty_info_id, p, major, index);
1179 : :
1180 [ + - ]: 19 : if (!ret)
1181 : 19 : ret = pb_write_one(fdset_fd(glob_fdset, CR_FD_TTY), &e, PB_TTY);
1182 : : return ret;
1183 : : }
1184 : :
1185 : : static const struct fdtype_ops tty_ops = {
1186 : : .type = FD_TYPES__TTY,
1187 : : .dump = dump_one_pty,
1188 : : };
1189 : :
1190 : 19 : int dump_tty(struct fd_parms *p, int lfd, const struct cr_fdset *set)
1191 : : {
1192 : 19 : return do_dump_gen_file(p, lfd, &tty_ops, set);
1193 : : }
1194 : :
1195 : 380 : int tty_prep_fds(void)
1196 : : {
1197 : 380 : self_stdin = get_service_fd(SELF_STDIN_OFF);
1198 : :
1199 [ - + ]: 380 : if (!isatty(STDIN_FILENO)) {
1200 : 0 : pr_err("Standart stream is not a terminal, aborting\n");
1201 : 0 : return -1;
1202 : : }
1203 : :
1204 [ - + ]: 380 : if (dup2(STDIN_FILENO, self_stdin) < 0) {
1205 : 0 : self_stdin = -1;
1206 : 0 : pr_perror("Can't dup stdin to SELF_STDIN_OFF");
1207 : 380 : return -1;
1208 : : }
1209 : :
1210 : : return 0;
1211 : : }
1212 : :
1213 : 349 : void tty_fini_fds(void)
1214 : : {
1215 : 349 : close_safe(&self_stdin);
1216 : 349 : }
|