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