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