]> git.proxmox.com Git - mirror_lxc.git/blob - src/lxc/console.c
Merge pull request #981 from LynxChaus/master
[mirror_lxc.git] / src / lxc / console.c
1 /*
2 * lxc: linux Container library
3 *
4 * (C) Copyright IBM Corp. 2007, 2008
5 *
6 * Authors:
7 * Daniel Lezcano <daniel.lezcano at free.fr>
8 *
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public
11 * License as published by the Free Software Foundation; either
12 * version 2.1 of the License, or (at your option) any later version.
13 *
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
18 *
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with this library; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
22 */
23
24 #include <assert.h>
25 #include <errno.h>
26 #include <fcntl.h>
27 #include <signal.h>
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <termios.h>
31 #include <unistd.h>
32 #include <sys/epoll.h>
33 #include <sys/types.h>
34
35 #include <lxc/lxccontainer.h>
36
37 #include "af_unix.h"
38 #include "caps.h"
39 #include "commands.h"
40 #include "conf.h"
41 #include "config.h"
42 #include "console.h"
43 #include "log.h"
44 #include "lxclock.h"
45 #include "mainloop.h"
46 #include "start.h" /* for struct lxc_handler */
47 #include "utils.h"
48
49 #if HAVE_PTY_H
50 #include <pty.h>
51 #else
52 #include <../include/openpty.h>
53 #endif
54
55 lxc_log_define(lxc_console, lxc);
56
57 static struct lxc_list lxc_ttys;
58
59 typedef void (*sighandler_t)(int);
60
61 __attribute__((constructor))
62 void lxc_console_init(void)
63 {
64 lxc_list_init(&lxc_ttys);
65 }
66
67 void lxc_console_winsz(int srcfd, int dstfd)
68 {
69 struct winsize wsz;
70 if (isatty(srcfd) && ioctl(srcfd, TIOCGWINSZ, &wsz) == 0) {
71 DEBUG("set winsz dstfd:%d cols:%d rows:%d", dstfd,
72 wsz.ws_col, wsz.ws_row);
73 ioctl(dstfd, TIOCSWINSZ, &wsz);
74 }
75 }
76
77 static void lxc_console_winch(struct lxc_tty_state *ts)
78 {
79 lxc_console_winsz(ts->stdinfd, ts->masterfd);
80 if (ts->winch_proxy)
81 lxc_cmd_console_winch(ts->winch_proxy, ts->winch_proxy_lxcpath);
82 }
83
84 void lxc_console_sigwinch(int sig)
85 {
86 struct lxc_list *it;
87 struct lxc_tty_state *ts;
88
89 lxc_list_for_each(it, &lxc_ttys) {
90 ts = it->elem;
91 lxc_console_winch(ts);
92 }
93 }
94
95 int lxc_console_cb_sigwinch_fd(int fd, uint32_t events, void *cbdata,
96 struct lxc_epoll_descr *descr)
97 {
98 struct signalfd_siginfo siginfo;
99 struct lxc_tty_state *ts = cbdata;
100
101 ssize_t ret = read(fd, &siginfo, sizeof(siginfo));
102 if (ret < 0 || (size_t)ret < sizeof(siginfo)) {
103 ERROR("failed to read signal info");
104 return -1;
105 }
106
107 lxc_console_winch(ts);
108 return 0;
109 }
110
111 struct lxc_tty_state *lxc_console_sigwinch_init(int srcfd, int dstfd)
112 {
113 sigset_t mask;
114 struct lxc_tty_state *ts;
115
116 ts = malloc(sizeof(*ts));
117 if (!ts)
118 return NULL;
119
120 memset(ts, 0, sizeof(*ts));
121 ts->stdinfd = srcfd;
122 ts->masterfd = dstfd;
123 ts->sigfd = -1;
124
125 /* add tty to list to be scanned at SIGWINCH time */
126 lxc_list_add_elem(&ts->node, ts);
127 lxc_list_add_tail(&lxc_ttys, &ts->node);
128
129 sigemptyset(&mask);
130 sigaddset(&mask, SIGWINCH);
131 if (sigprocmask(SIG_BLOCK, &mask, &ts->oldmask)) {
132 SYSERROR("failed to block SIGWINCH.");
133 ts->sigfd = -1;
134 return ts;
135 }
136
137 ts->sigfd = signalfd(-1, &mask, 0);
138 if (ts->sigfd < 0) {
139 SYSERROR("failed to get signalfd.");
140 sigprocmask(SIG_SETMASK, &ts->oldmask, NULL);
141 ts->sigfd = -1;
142 return ts;
143 }
144
145 DEBUG("%d got SIGWINCH fd %d", getpid(), ts->sigfd);
146 return ts;
147 }
148
149 void lxc_console_sigwinch_fini(struct lxc_tty_state *ts)
150 {
151 if (ts->sigfd >= 0)
152 close(ts->sigfd);
153
154 lxc_list_del(&ts->node);
155 sigprocmask(SIG_SETMASK, &ts->oldmask, NULL);
156 free(ts);
157 }
158
159 static int lxc_console_cb_con(int fd, uint32_t events, void *data,
160 struct lxc_epoll_descr *descr)
161 {
162 struct lxc_console *console = (struct lxc_console *)data;
163 char buf[1024];
164 int r, w;
165
166 w = r = lxc_read_nointr(fd, buf, sizeof(buf));
167 if (r <= 0) {
168 INFO("console client on fd %d has exited", fd);
169 lxc_mainloop_del_handler(descr, fd);
170 close(fd);
171 return 1;
172 }
173
174 if (fd == console->peer)
175 w = lxc_write_nointr(console->master, buf, r);
176
177 if (fd == console->master) {
178 if (console->log_fd >= 0)
179 w = lxc_write_nointr(console->log_fd, buf, r);
180
181 if (console->peer >= 0)
182 w = lxc_write_nointr(console->peer, buf, r);
183 }
184
185 if (w != r)
186 WARN("console short write r:%d w:%d", r, w);
187
188 return 0;
189 }
190
191 static void lxc_console_mainloop_add_peer(struct lxc_console *console)
192 {
193 if (console->peer >= 0) {
194 if (lxc_mainloop_add_handler(console->descr, console->peer,
195 lxc_console_cb_con, console))
196 WARN("console peer not added to mainloop");
197 }
198
199 if (console->tty_state && console->tty_state->sigfd != -1) {
200 if (lxc_mainloop_add_handler(console->descr,
201 console->tty_state->sigfd,
202 lxc_console_cb_sigwinch_fd,
203 console->tty_state)) {
204 WARN("failed to add to mainloop SIGWINCH handler for '%d'",
205 console->tty_state->sigfd);
206 }
207 }
208 }
209
210 extern int lxc_console_mainloop_add(struct lxc_epoll_descr *descr,
211 struct lxc_conf *conf)
212 {
213 struct lxc_console *console = &conf->console;
214
215 if (conf->is_execute) {
216 INFO("no console for lxc-execute.");
217 return 0;
218 }
219
220 if (!conf->rootfs.path) {
221 INFO("no rootfs, no console.");
222 return 0;
223 }
224
225 if (console->master < 0) {
226 INFO("no console");
227 return 0;
228 }
229
230 if (lxc_mainloop_add_handler(descr, console->master,
231 lxc_console_cb_con, console)) {
232 ERROR("failed to add to mainloop console handler for '%d'",
233 console->master);
234 return -1;
235 }
236
237 /* we cache the descr so that we can add an fd to it when someone
238 * does attach to it in lxc_console_allocate()
239 */
240 console->descr = descr;
241 lxc_console_mainloop_add_peer(console);
242
243 return 0;
244 }
245
246 int lxc_setup_tios(int fd, struct termios *oldtios)
247 {
248 struct termios newtios;
249
250 if (!isatty(fd)) {
251 ERROR("'%d' is not a tty", fd);
252 return -1;
253 }
254
255 /* Get current termios */
256 if (tcgetattr(fd, oldtios)) {
257 SYSERROR("failed to get current terminal settings");
258 return -1;
259 }
260
261 newtios = *oldtios;
262
263 /* Remove the echo characters and signal reception, the echo
264 * will be done with master proxying */
265 newtios.c_iflag &= ~(IGNBRK|ISTRIP|INLCR|IGNCR|ICRNL|IXON|IXANY|IXOFF);
266 newtios.c_iflag &= BRKINT;
267 newtios.c_lflag &= ~(ECHO|ICANON|ISIG|IEXTEN|ECHOE|ECHOK|ECHONL);
268 newtios.c_oflag &= ~OPOST;
269 newtios.c_cc[VMIN] = 1;
270 newtios.c_cc[VTIME] = 0;
271
272 /* Set new attributes */
273 if (tcsetattr(fd, TCSAFLUSH, &newtios)) {
274 ERROR("failed to set new terminal settings");
275 return -1;
276 }
277
278 return 0;
279 }
280
281 static void lxc_console_peer_proxy_free(struct lxc_console *console)
282 {
283 if (console->tty_state && console->tty_state->sigfd != -1) {
284 lxc_console_sigwinch_fini(console->tty_state);
285 console->tty_state = NULL;
286 }
287 close(console->peerpty.master);
288 close(console->peerpty.slave);
289 console->peerpty.master = -1;
290 console->peerpty.slave = -1;
291 console->peerpty.busy = -1;
292 console->peerpty.name[0] = '\0';
293 console->peer = -1;
294 }
295
296 static int lxc_console_peer_proxy_alloc(struct lxc_console *console, int sockfd)
297 {
298 struct termios oldtermio;
299 struct lxc_tty_state *ts;
300 int ret;
301
302 if (console->master < 0) {
303 ERROR("console not set up");
304 return -1;
305 }
306 if (console->peerpty.busy != -1 || console->peer != -1) {
307 NOTICE("console already in use");
308 return -1;
309 }
310 if (console->tty_state) {
311 ERROR("console already has tty_state");
312 return -1;
313 }
314
315 /* this is the proxy pty that will be given to the client, and that
316 * the real pty master will send to / recv from
317 */
318 process_lock();
319 ret = openpty(&console->peerpty.master, &console->peerpty.slave,
320 console->peerpty.name, NULL, NULL);
321 process_unlock();
322 if (ret) {
323 SYSERROR("failed to create proxy pty");
324 return -1;
325 }
326
327 if (lxc_setup_tios(console->peerpty.slave, &oldtermio) < 0)
328 goto err1;
329
330 ts = lxc_console_sigwinch_init(console->peerpty.master, console->master);
331 if (!ts)
332 goto err1;
333
334 console->tty_state = ts;
335 console->peer = console->peerpty.slave;
336 console->peerpty.busy = sockfd;
337 lxc_console_mainloop_add_peer(console);
338
339 DEBUG("%d %s peermaster:%d sockfd:%d", getpid(), __FUNCTION__, console->peerpty.master, sockfd);
340 return 0;
341
342 err1:
343 lxc_console_peer_proxy_free(console);
344 return -1;
345 }
346
347 int lxc_console_allocate(struct lxc_conf *conf, int sockfd, int *ttyreq)
348 {
349 int masterfd = -1, ttynum;
350 struct lxc_tty_info *tty_info = &conf->tty_info;
351 struct lxc_console *console = &conf->console;
352
353 if (*ttyreq == 0) {
354 if (lxc_console_peer_proxy_alloc(console, sockfd) < 0)
355 goto out;
356 masterfd = console->peerpty.master;
357 goto out;
358 }
359
360 if (*ttyreq > 0) {
361 if (*ttyreq > tty_info->nbtty)
362 goto out;
363
364 if (tty_info->pty_info[*ttyreq - 1].busy)
365 goto out;
366
367 /* the requested tty is available */
368 ttynum = *ttyreq;
369 goto out_tty;
370 }
371
372 /* search for next available tty, fixup index tty1 => [0] */
373 for (ttynum = 1; ttynum <= tty_info->nbtty && tty_info->pty_info[ttynum - 1].busy; ttynum++)
374 ;
375
376 /* we didn't find any available slot for tty */
377 if (ttynum > tty_info->nbtty)
378 goto out;
379
380 *ttyreq = ttynum;
381
382 out_tty:
383 tty_info->pty_info[ttynum - 1].busy = sockfd;
384 masterfd = tty_info->pty_info[ttynum - 1].master;
385 out:
386 return masterfd;
387 }
388
389 void lxc_console_free(struct lxc_conf *conf, int fd)
390 {
391 int i;
392 struct lxc_tty_info *tty_info = &conf->tty_info;
393 struct lxc_console *console = &conf->console;
394
395 for (i = 0; i < tty_info->nbtty; i++) {
396 if (tty_info->pty_info[i].busy == fd)
397 tty_info->pty_info[i].busy = 0;
398 }
399
400 if (console->peerpty.busy == fd) {
401 lxc_mainloop_del_handler(console->descr, console->peerpty.slave);
402 lxc_console_peer_proxy_free(console);
403 }
404 }
405
406 static void lxc_console_peer_default(struct lxc_console *console)
407 {
408 struct lxc_tty_state *ts;
409 const char *path = console->path;
410
411 /* if no console was given, try current controlling terminal, there
412 * won't be one if we were started as a daemon (-d)
413 */
414 if (!path && !access("/dev/tty", F_OK)) {
415 int fd;
416 fd = open("/dev/tty", O_RDWR);
417 if (fd >= 0) {
418 close(fd);
419 path = "/dev/tty";
420 }
421 }
422
423 if (!path)
424 goto out;
425
426 DEBUG("opening %s for console peer", path);
427 console->peer = lxc_unpriv(open(path, O_CLOEXEC | O_RDWR | O_CREAT |
428 O_APPEND, 0600));
429 if (console->peer < 0)
430 goto out;
431
432 DEBUG("using '%s' as console", path);
433
434 if (!isatty(console->peer))
435 goto err1;
436
437 ts = lxc_console_sigwinch_init(console->peer, console->master);
438 console->tty_state = ts;
439 if (!ts) {
440 WARN("Unable to install SIGWINCH");
441 goto err1;
442 }
443
444 lxc_console_winsz(console->peer, console->master);
445
446 console->tios = malloc(sizeof(*console->tios));
447 if (!console->tios) {
448 SYSERROR("failed to allocate memory");
449 goto err1;
450 }
451
452 if (lxc_setup_tios(console->peer, console->tios) < 0)
453 goto err2;
454
455 return;
456
457 err2:
458 free(console->tios);
459 console->tios = NULL;
460 err1:
461 close(console->peer);
462 console->peer = -1;
463 out:
464 DEBUG("no console peer");
465 return;
466 }
467
468 void lxc_console_delete(struct lxc_console *console)
469 {
470 if (console->tios && console->peer >= 0 &&
471 tcsetattr(console->peer, TCSAFLUSH, console->tios))
472 WARN("failed to set old terminal settings");
473 free(console->tios);
474 console->tios = NULL;
475
476 close(console->peer);
477 close(console->master);
478 close(console->slave);
479 if (console->log_fd >= 0)
480 close(console->log_fd);
481
482 console->peer = -1;
483 console->master = -1;
484 console->slave = -1;
485 console->log_fd = -1;
486 }
487
488 int lxc_console_create(struct lxc_conf *conf)
489 {
490 struct lxc_console *console = &conf->console;
491 int ret;
492
493 if (conf->is_execute) {
494 INFO("no console for lxc-execute.");
495 return 0;
496 }
497
498 if (!conf->rootfs.path)
499 return 0;
500
501 if (console->path && !strcmp(console->path, "none"))
502 return 0;
503
504 process_lock();
505 ret = openpty(&console->master, &console->slave,
506 console->name, NULL, NULL);
507 process_unlock();
508 if (ret) {
509 SYSERROR("failed to allocate a pty");
510 return -1;
511 }
512
513 if (fcntl(console->master, F_SETFD, FD_CLOEXEC)) {
514 SYSERROR("failed to set console master to close-on-exec");
515 goto err;
516 }
517
518 if (fcntl(console->slave, F_SETFD, FD_CLOEXEC)) {
519 SYSERROR("failed to set console slave to close-on-exec");
520 goto err;
521 }
522
523 lxc_console_peer_default(console);
524
525 if (console->log_path) {
526 console->log_fd = lxc_unpriv(open(console->log_path,
527 O_CLOEXEC | O_RDWR |
528 O_CREAT | O_APPEND, 0600));
529 if (console->log_fd < 0) {
530 SYSERROR("failed to open '%s'", console->log_path);
531 goto err;
532 }
533 DEBUG("using '%s' as console log", console->log_path);
534 }
535
536 return 0;
537
538 err:
539 lxc_console_delete(console);
540 return -1;
541 }
542
543 int lxc_console_set_stdfds(int fd)
544 {
545 if (fd < 0)
546 return 0;
547
548 if (isatty(STDIN_FILENO))
549 if (dup2(fd, STDIN_FILENO) < 0) {
550 SYSERROR("failed to duplicate stdin.");
551 return -1;
552 }
553
554 if (isatty(STDOUT_FILENO))
555 if (dup2(fd, STDOUT_FILENO) < 0) {
556 SYSERROR("failed to duplicate stdout.");
557 return -1;
558 }
559
560 if (isatty(STDERR_FILENO))
561 if (dup2(fd, STDERR_FILENO) < 0) {
562 SYSERROR("failed to duplicate stderr.");
563 return -1;
564 }
565
566 return 0;
567 }
568
569 int lxc_console_cb_tty_stdin(int fd, uint32_t events, void *cbdata,
570 struct lxc_epoll_descr *descr)
571 {
572 struct lxc_tty_state *ts = cbdata;
573 char c;
574
575 assert(fd == ts->stdinfd);
576 if (lxc_read_nointr(ts->stdinfd, &c, 1) <= 0)
577 return 1;
578
579 if (ts->escape != -1) {
580 /* we want to exit the console with Ctrl+a q */
581 if (c == ts->escape && !ts->saw_escape) {
582 ts->saw_escape = 1;
583 return 0;
584 }
585
586 if (c == 'q' && ts->saw_escape)
587 return 1;
588
589 ts->saw_escape = 0;
590 }
591
592 if (lxc_write_nointr(ts->masterfd, &c, 1) <= 0)
593 return 1;
594
595 return 0;
596 }
597
598 int lxc_console_cb_tty_master(int fd, uint32_t events, void *cbdata,
599 struct lxc_epoll_descr *descr)
600 {
601 struct lxc_tty_state *ts = cbdata;
602 char buf[1024];
603 int r, w;
604
605 assert(fd == ts->masterfd);
606 r = lxc_read_nointr(fd, buf, sizeof(buf));
607 if (r <= 0)
608 return 1;
609
610 w = lxc_write_nointr(ts->stdoutfd, buf, r);
611 if (w <= 0) {
612 return 1;
613 } else if (w != r) {
614 SYSERROR("failed to write");
615 return 1;
616 }
617
618 return 0;
619 }
620
621 int lxc_console_getfd(struct lxc_container *c, int *ttynum, int *masterfd)
622 {
623 return lxc_cmd_console(c->name, ttynum, masterfd, c->config_path);
624 }
625
626 int lxc_console(struct lxc_container *c, int ttynum,
627 int stdinfd, int stdoutfd, int stderrfd,
628 int escape)
629 {
630 int ret, ttyfd, masterfd;
631 struct lxc_epoll_descr descr;
632 struct termios oldtios;
633 struct lxc_tty_state *ts;
634
635 if (!isatty(stdinfd)) {
636 ERROR("stdin is not a tty");
637 return -1;
638 }
639
640 ret = lxc_setup_tios(stdinfd, &oldtios);
641 if (ret) {
642 ERROR("failed to setup tios");
643 return -1;
644 }
645
646 ttyfd = lxc_cmd_console(c->name, &ttynum, &masterfd, c->config_path);
647 if (ttyfd < 0) {
648 ret = ttyfd;
649 goto err1;
650 }
651
652 fprintf(stderr, "\n"
653 "Connected to tty %1$d\n"
654 "Type <Ctrl+%2$c q> to exit the console, "
655 "<Ctrl+%2$c Ctrl+%2$c> to enter Ctrl+%2$c itself\n",
656 ttynum, 'a' + escape - 1);
657
658 ret = setsid();
659 if (ret)
660 INFO("already group leader");
661
662 ts = lxc_console_sigwinch_init(stdinfd, masterfd);
663 if (!ts) {
664 ret = -1;
665 goto err2;
666 }
667 ts->escape = escape;
668 ts->winch_proxy = c->name;
669 ts->winch_proxy_lxcpath = c->config_path;
670
671 lxc_console_winsz(stdinfd, masterfd);
672 lxc_cmd_console_winch(ts->winch_proxy, ts->winch_proxy_lxcpath);
673
674 ret = lxc_mainloop_open(&descr);
675 if (ret) {
676 ERROR("failed to create mainloop");
677 goto err3;
678 }
679
680 if (ts->sigfd != -1) {
681 ret = lxc_mainloop_add_handler(&descr, ts->sigfd,
682 lxc_console_cb_sigwinch_fd, ts);
683 if (ret) {
684 ERROR("failed to add handler for SIGWINCH fd");
685 goto err4;
686 }
687 }
688
689 ret = lxc_mainloop_add_handler(&descr, ts->stdinfd,
690 lxc_console_cb_tty_stdin, ts);
691 if (ret) {
692 ERROR("failed to add handler for stdinfd");
693 goto err4;
694 }
695
696 ret = lxc_mainloop_add_handler(&descr, ts->masterfd,
697 lxc_console_cb_tty_master, ts);
698 if (ret) {
699 ERROR("failed to add handler for masterfd");
700 goto err4;
701 }
702
703 ret = lxc_mainloop(&descr, -1);
704 if (ret) {
705 ERROR("mainloop returned an error");
706 goto err4;
707 }
708
709 ret = 0;
710
711 err4:
712 lxc_mainloop_close(&descr);
713 err3:
714 if (ts->sigfd != -1)
715 lxc_console_sigwinch_fini(ts);
716 err2:
717 close(masterfd);
718 close(ttyfd);
719 err1:
720 tcsetattr(stdinfd, TCSAFLUSH, &oldtios);
721
722 return ret;
723 }
724