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