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