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