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