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