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