]> git.proxmox.com Git - mirror_lxc.git/blame - src/lxc/terminal.c
tools: fix build warnings
[mirror_lxc.git] / src / lxc / terminal.c
CommitLineData
cc73685d 1/* SPDX-License-Identifier: LGPL-2.1+ */
b0a33c1e 2
d38dd64a
CB
3#ifndef _GNU_SOURCE
4#define _GNU_SOURCE 1
5#endif
9395937a
CB
6#include <errno.h>
7#include <fcntl.h>
b467714b 8#include <pthread.h>
b5159817 9#include <signal.h>
b0a33c1e 10#include <stdio.h>
e0dc0de7 11#include <stdlib.h>
da41561c
CB
12#include <sys/epoll.h>
13#include <sys/types.h>
e62912bd
CB
14#include <termios.h>
15#include <unistd.h>
f2363e38 16
12ae2a33
CB
17#include "lxc.h"
18
9395937a
CB
19#include "af_unix.h"
20#include "caps.h"
21#include "commands.h"
00dbc43e 22#include "conf.h"
e827ff7e 23#include "config.h"
9395937a 24#include "log.h"
b5159817 25#include "lxclock.h"
9395937a 26#include "mainloop.h"
2530ba95 27#include "memory_utils.h"
e62912bd 28#include "start.h"
303037d2 29#include "syscall_wrappers.h"
0ed9b1bc 30#include "terminal.h"
b5159817 31#include "utils.h"
36eb9bde 32
35eb5cdc 33#if HAVE_OPENPTY
e827ff7e
SG
34#include <pty.h>
35#else
58db1a61 36#include "openpty.h"
e827ff7e
SG
37#endif
38
de708fb7 39#define LXC_TERMINAL_BUFFER_SIZE 1024
732375f5 40
2083d59d 41lxc_log_define(terminal, lxc);
36eb9bde 42
4e9c0330 43void lxc_terminal_winsz(int srcfd, int dstfd)
b5159817 44{
0519b5cc 45 int ret;
b5159817 46 struct winsize wsz;
0519b5cc
CB
47
48 if (!isatty(srcfd))
49 return;
50
51 ret = ioctl(srcfd, TIOCGWINSZ, &wsz);
52 if (ret < 0) {
53 WARN("Failed to get window size");
54 return;
724e753c 55 }
0519b5cc
CB
56
57 ret = ioctl(dstfd, TIOCSWINSZ, &wsz);
58 if (ret < 0)
59 WARN("Failed to set window size");
60 else
61 DEBUG("Set window size to %d columns and %d rows", wsz.ws_col,
62 wsz.ws_row);
63
64 return;
b5159817 65}
724e753c 66
5b55021f 67static void lxc_terminal_winch(struct lxc_terminal_state *ts)
b5159817 68{
36a94ce8 69 lxc_terminal_winsz(ts->stdinfd, ts->ptxfd);
b5159817 70}
cd453b38 71
9bafc8cb 72int lxc_terminal_signalfd_cb(int fd, uint32_t events, void *cbdata,
3298b37d 73 struct lxc_async_descr *descr)
b5159817 74{
1349e92e 75 ssize_t ret;
b5159817 76 struct signalfd_siginfo siginfo;
5b55021f 77 struct lxc_terminal_state *ts = cbdata;
b5159817 78
72f7c19b 79 ret = lxc_read_nointr(fd, &siginfo, sizeof(siginfo));
0d4137cc 80 if (ret < 0 || (size_t)ret < sizeof(siginfo)) {
0519b5cc 81 ERROR("Failed to read signal info");
2e943b7b 82 return LXC_MAINLOOP_ERROR;
cd453b38
DL
83 }
84
1349e92e 85 if (siginfo.ssi_signo == SIGTERM) {
9bafc8cb 86 DEBUG("Received SIGTERM. Detaching from the terminal");
a529bc25 87 return LXC_MAINLOOP_CLOSE;
1349e92e
CB
88 }
89
90 if (siginfo.ssi_signo == SIGWINCH)
7a10164a 91 lxc_terminal_winch(ts);
1349e92e 92
2e943b7b 93 return LXC_MAINLOOP_CONTINUE;
b5159817
DE
94}
95
5b55021f 96struct lxc_terminal_state *lxc_terminal_signal_init(int srcfd, int dstfd)
b5159817 97{
f62cf1d4 98 __do_close int signal_fd = -EBADF;
28327a43 99 __do_free struct lxc_terminal_state *ts = NULL;
28327a43 100 int ret;
b5159817 101 sigset_t mask;
b5159817
DE
102
103 ts = malloc(sizeof(*ts));
104 if (!ts)
105 return NULL;
106
107 memset(ts, 0, sizeof(*ts));
341c2aed 108 ts->stdinfd = srcfd;
36a94ce8 109 ts->ptxfd = dstfd;
341c2aed 110 ts->sigfd = -1;
b5159817 111
b6d5de95
CB
112 ret = sigemptyset(&mask);
113 if (ret < 0) {
114 SYSERROR("Failed to initialize an empty signal set");
1ba4ae89 115 return NULL;
b6d5de95 116 }
1349e92e 117
1ba4ae89 118 if (isatty(srcfd)) {
b6d5de95
CB
119 ret = sigaddset(&mask, SIGWINCH);
120 if (ret < 0)
7874d81a 121 SYSNOTICE("Failed to add SIGWINCH to signal set");
1ba4ae89
CB
122 } else {
123 INFO("fd %d does not refer to a tty device", srcfd);
25964232
LF
124 }
125
1349e92e 126 /* Exit the mainloop cleanly on SIGTERM. */
b6d5de95
CB
127 ret = sigaddset(&mask, SIGTERM);
128 if (ret < 0) {
129 SYSERROR("Failed to add SIGWINCH to signal set");
1ba4ae89 130 return NULL;
b6d5de95 131 }
b5159817 132
b467714b 133 ret = pthread_sigmask(SIG_BLOCK, &mask, &ts->oldmask);
1349e92e
CB
134 if (ret < 0) {
135 WARN("Failed to block signals");
1ba4ae89 136 return NULL;
b5159817
DE
137 }
138
1ba4ae89
CB
139 signal_fd = signalfd(-1, &mask, SFD_CLOEXEC);
140 if (signal_fd < 0) {
1349e92e 141 WARN("Failed to create signal fd");
b467714b 142 (void)pthread_sigmask(SIG_SETMASK, &ts->oldmask, NULL);
1ba4ae89 143 return NULL;
1349e92e 144 }
1ba4ae89
CB
145 ts->sigfd = move_fd(signal_fd);
146 TRACE("Created signal fd %d", ts->sigfd);
b6d5de95 147
1ba4ae89 148 return move_ptr(ts);
cd453b38
DL
149}
150
3e3f79bd
CB
151int lxc_terminal_signal_sigmask_safe_blocked(struct lxc_terminal *terminal)
152{
153 struct lxc_terminal_state *state = terminal->tty_state;
154
155 if (!state)
156 return 0;
157
158 return pthread_sigmask(SIG_SETMASK, &state->oldmask, NULL);
159}
160
26ed61e0
CB
161/**
162 * lxc_terminal_signal_fini: uninstall signal handler
163 *
164 * @terminal: terminal instance
165 *
166 * Restore the saved signal handler that was in effect at the time
167 * lxc_terminal_signal_init() was called.
168 */
169static void lxc_terminal_signal_fini(struct lxc_terminal *terminal)
63376d7d 170{
28327a43 171 struct lxc_terminal_state *state = terminal->tty_state;
1349e92e 172
28327a43
CB
173 if (!terminal->tty_state)
174 return;
175
176 state = terminal->tty_state;
177 if (state->sigfd >= 0) {
178 close(state->sigfd);
179
180 if (pthread_sigmask(SIG_SETMASK, &state->oldmask, NULL) < 0)
a24c5678 181 SYSWARN("Failed to restore signal mask");
0e6da90b 182 }
0d4137cc 183
28327a43
CB
184 free(terminal->tty_state);
185 terminal->tty_state = NULL;
b5159817 186}
1560f6c9 187
99a04585 188static int lxc_terminal_truncate_log_file(struct lxc_terminal *terminal)
861813e5
CB
189{
190 /* be very certain things are kosher */
2083d59d 191 if (!terminal->log_path || terminal->log_fd < 0)
861813e5
CB
192 return -EBADF;
193
2083d59d 194 return lxc_unpriv(ftruncate(terminal->log_fd, 0));
861813e5
CB
195}
196
468724d3 197static int lxc_terminal_rotate_log_file(struct lxc_terminal *terminal)
861813e5 198{
2530ba95 199 __do_free char *tmp = NULL;
861813e5
CB
200 int ret;
201 size_t len;
861813e5 202
99a04585 203 if (!terminal->log_path || terminal->log_rotate == 0)
861813e5
CB
204 return -EOPNOTSUPP;
205
206 /* be very certain things are kosher */
99a04585 207 if (terminal->log_fd < 0)
861813e5
CB
208 return -EBADF;
209
99a04585 210 len = strlen(terminal->log_path) + sizeof(".1");
2530ba95 211 tmp = must_realloc(NULL, len);
861813e5 212
93d5ebf1
CB
213 ret = strnprintf(tmp, len, "%s.1", terminal->log_path);
214 if (ret < 0)
861813e5
CB
215 return -EFBIG;
216
99a04585
CB
217 close(terminal->log_fd);
218 terminal->log_fd = -1;
219 ret = lxc_unpriv(rename(terminal->log_path, tmp));
861813e5
CB
220 if (ret < 0)
221 return ret;
222
99a04585 223 return lxc_terminal_create_log_file(terminal);
861813e5
CB
224}
225
a44ae1a9 226static int lxc_terminal_write_log_file(struct lxc_terminal *terminal, char *buf,
8903fb08 227 int bytes_read)
861813e5
CB
228{
229 int ret;
861813e5 230 struct stat st;
8903fb08 231 int64_t space_left = -1;
861813e5 232
99a04585 233 if (terminal->log_fd < 0)
861813e5
CB
234 return 0;
235
236 /* A log size <= 0 means that there's no limit on the size of the log
237 * file at which point we simply ignore whether the log is supposed to
238 * be rotated or not.
239 */
99a04585
CB
240 if (terminal->log_size <= 0)
241 return lxc_write_nointr(terminal->log_fd, buf, bytes_read);
861813e5
CB
242
243 /* Get current size of the log file. */
99a04585 244 ret = fstat(terminal->log_fd, &st);
861813e5 245 if (ret < 0) {
99a04585 246 SYSERROR("Failed to stat the terminal log file descriptor");
861813e5
CB
247 return -1;
248 }
249
250 /* handle non-regular files */
251 if ((st.st_mode & S_IFMT) != S_IFREG) {
252 /* This isn't a regular file. so rotating the file seems a
253 * dangerous thing to do, size limits are also very
254 * questionable. Let's not risk anything and tell the user that
f48e8071 255 * they're requesting us to do weird stuff.
861813e5 256 */
99a04585 257 if (terminal->log_rotate > 0 || terminal->log_size > 0)
861813e5
CB
258 return -EINVAL;
259
260 /* I mean, sure log wherever you want to. */
99a04585 261 return lxc_write_nointr(terminal->log_fd, buf, bytes_read);
861813e5
CB
262 }
263
99a04585 264 space_left = terminal->log_size - st.st_size;
861813e5
CB
265
266 /* User doesn't want to rotate the log file and there's no more space
267 * left so simply truncate it.
268 */
99a04585
CB
269 if (space_left <= 0 && terminal->log_rotate <= 0) {
270 ret = lxc_terminal_truncate_log_file(terminal);
861813e5
CB
271 if (ret < 0)
272 return ret;
273
8703bf5b 274 if ((uint64_t)bytes_read <= terminal->log_size)
99a04585 275 return lxc_write_nointr(terminal->log_fd, buf, bytes_read);
861813e5
CB
276
277 /* Write as much as we can into the buffer and loose the rest. */
99a04585 278 return lxc_write_nointr(terminal->log_fd, buf, terminal->log_size);
861813e5
CB
279 }
280
281 /* There's enough space left. */
282 if (bytes_read <= space_left)
99a04585 283 return lxc_write_nointr(terminal->log_fd, buf, bytes_read);
861813e5
CB
284
285 /* There's not enough space left but at least write as much as we can
286 * into the old log file.
287 */
99a04585 288 ret = lxc_write_nointr(terminal->log_fd, buf, space_left);
861813e5
CB
289 if (ret < 0)
290 return -1;
291
292 /* Calculate how many bytes we still need to write. */
293 bytes_read -= space_left;
294
89962c6c 295 /* There'd be more to write but we aren't instructed to rotate the log
861813e5
CB
296 * file so simply return. There's no error on our side here.
297 */
99a04585 298 if (terminal->log_rotate > 0)
468724d3 299 ret = lxc_terminal_rotate_log_file(terminal);
861813e5 300 else
99a04585 301 ret = lxc_terminal_truncate_log_file(terminal);
861813e5
CB
302 if (ret < 0)
303 return ret;
304
8703bf5b 305 if (terminal->log_size < (uint64_t)bytes_read) {
861813e5
CB
306 /* Well, this is unfortunate because it means that there is more
307 * to write than the user has granted us space. There are
308 * multiple ways to handle this but let's use the simplest one:
309 * write as much as we can, tell the user that there was more
310 * stuff to write and move on.
311 * Note that this scenario shouldn't actually happen with the
99a04585 312 * standard pty-based terminal that LXC allocates since it will
861813e5
CB
313 * be switched into raw mode. In raw mode only 1 byte at a time
314 * should be read and written.
315 */
99a04585
CB
316 WARN("Size of terminal log file is smaller than the bytes to write");
317 ret = lxc_write_nointr(terminal->log_fd, buf, terminal->log_size);
861813e5
CB
318 if (ret < 0)
319 return -1;
320 bytes_read -= ret;
321 return bytes_read;
322 }
323
324 /* Yay, we made it. */
99a04585 325 ret = lxc_write_nointr(terminal->log_fd, buf, bytes_read);
861813e5
CB
326 if (ret < 0)
327 return -1;
328 bytes_read -= ret;
329 return bytes_read;
330}
331
543d2f83 332static int lxc_terminal_ptx_io(struct lxc_terminal *terminal)
b5159817 333{
de708fb7 334 char buf[LXC_TERMINAL_BUFFER_SIZE];
732375f5 335 int r, w, w_log, w_rbuf;
e0dc0de7 336
543d2f83
CB
337 w = r = lxc_read_nointr(terminal->ptx, buf, sizeof(buf));
338 if (r <= 0)
339 return -1;
c06a0555 340
543d2f83
CB
341 w_rbuf = w_log = 0;
342 /* write to peer first */
343 if (terminal->peer >= 0)
344 w = lxc_write_nointr(terminal->peer, buf, r);
63376d7d 345
543d2f83
CB
346 /* write to terminal ringbuffer */
347 if (terminal->buffer_size > 0)
348 w_rbuf = lxc_ringbuf_write(&terminal->ringbuf, buf, r);
b5159817 349
543d2f83
CB
350 /* write to terminal log */
351 if (terminal->log_fd >= 0)
352 w_log = lxc_terminal_write_log_file(terminal, buf, r);
63376d7d 353
b5159817 354 if (w != r)
de708fb7 355 WARN("Short write on terminal r:%d != w:%d", r, w);
732375f5 356
7874d81a 357 if (w_rbuf < 0) {
358 errno = -w_rbuf;
359 SYSTRACE("Failed to write %d bytes to terminal ringbuffer", r);
360 }
732375f5
CB
361
362 if (w_log < 0)
de708fb7 363 TRACE("Failed to write %d bytes to terminal log", r);
0d4137cc 364
543d2f83
CB
365 return 0;
366}
367
368static int lxc_terminal_peer_io(struct lxc_terminal *terminal)
369{
370 char buf[LXC_TERMINAL_BUFFER_SIZE];
371 int r, w;
372
373 w = r = lxc_read_nointr(terminal->peer, buf, sizeof(buf));
374 if (r <= 0)
375 return -1;
376
377 w = lxc_write_nointr(terminal->ptx, buf, r);
378 if (w != r)
379 WARN("Short write on terminal r:%d != w:%d", r, w);
380
381 return 0;
382}
383
384static int lxc_terminal_ptx_io_handler(int fd, uint32_t events, void *data,
385 struct lxc_async_descr *descr)
386{
387 struct lxc_terminal *terminal = data;
388 int ret;
389
390 ret = lxc_terminal_ptx_io(data);
391 if (ret < 0)
392 return log_info(LXC_MAINLOOP_CLOSE,
393 "Terminal client on fd %d has exited",
394 terminal->ptx);
395
396 return LXC_MAINLOOP_CONTINUE;
397}
398
399static int lxc_terminal_peer_io_handler(int fd, uint32_t events, void *data,
400 struct lxc_async_descr *descr)
401{
402 struct lxc_terminal *terminal = data;
403 int ret;
404
405 ret = lxc_terminal_peer_io(data);
406 if (ret < 0)
407 return log_info(LXC_MAINLOOP_CLOSE,
408 "Terminal client on fd %d has exited",
409 terminal->peer);
410
2b8bf299 411 return LXC_MAINLOOP_CONTINUE;
b5159817
DE
412}
413
dcad02f8 414static int lxc_terminal_mainloop_add_peer(struct lxc_terminal *terminal)
b5159817 415{
a529bc25
CB
416 int ret;
417
2083d59d
CB
418 if (terminal->peer >= 0) {
419 ret = lxc_mainloop_add_handler(terminal->descr, terminal->peer,
543d2f83
CB
420 lxc_terminal_peer_io_handler,
421 default_cleanup_handler,
422 terminal, "lxc_terminal_peer_io_handler");
a529bc25 423 if (ret < 0) {
2083d59d 424 WARN("Failed to add terminal peer handler to mainloop");
a529bc25
CB
425 return -1;
426 }
63376d7d
DL
427 }
428
2083d59d 429 if (!terminal->tty_state || terminal->tty_state->sigfd < 0)
a529bc25
CB
430 return 0;
431
543d2f83
CB
432 ret = lxc_mainloop_add_handler(terminal->descr,
433 terminal->tty_state->sigfd,
434 lxc_terminal_signalfd_cb,
435 default_cleanup_handler,
436 terminal->tty_state,
437 "lxc_terminal_signalfd_cb");
a529bc25
CB
438 if (ret < 0) {
439 WARN("Failed to add signal handler to mainloop");
440 return -1;
b5159817 441 }
a529bc25
CB
442
443 return 0;
b5159817
DE
444}
445
3298b37d 446int lxc_terminal_mainloop_add(struct lxc_async_descr *descr,
ea5b3c23 447 struct lxc_terminal *terminal)
b5159817 448{
a529bc25 449 int ret;
b5159817 450
36a94ce8 451 if (terminal->ptx < 0) {
2083d59d 452 INFO("Terminal is not initialized");
b5159817 453 return 0;
596a818d
DE
454 }
455
36a94ce8 456 ret = lxc_mainloop_add_handler(descr, terminal->ptx,
543d2f83
CB
457 lxc_terminal_ptx_io_handler,
458 default_cleanup_handler,
459 terminal, "lxc_terminal_ptx_io_handler");
30a33fbd 460 if (ret < 0) {
543d2f83 461 ERROR("Failed to add handler for terminal ptx fd %d to mainloop", terminal->ptx);
b5159817 462 return -1;
28a4b0e5
DL
463 }
464
30a33fbd 465 /* We cache the descr so that we can add an fd to it when someone
c1ee47cd 466 * does attach to it in lxc_terminal_allocate().
b5159817 467 */
2083d59d 468 terminal->descr = descr;
cd453b38 469
ea5b3c23 470 return lxc_terminal_mainloop_add_peer(terminal);
b5159817 471}
28a4b0e5 472
0d4137cc 473int lxc_setup_tios(int fd, struct termios *oldtios)
b5159817 474{
e4953e62 475 int ret;
b5159817 476 struct termios newtios;
e0dc0de7 477
b5159817 478 if (!isatty(fd)) {
8332a09c 479 ERROR("File descriptor %d does not refer to a terminal", fd);
b5159817 480 return -1;
e0dc0de7
DL
481 }
482
e4953e62
CB
483 /* Get current termios. */
484 ret = tcgetattr(fd, oldtios);
485 if (ret < 0) {
486 SYSERROR("Failed to get current terminal settings");
b5159817 487 return -1;
e0dc0de7
DL
488 }
489
4dc96430
TJ
490 /* ensure we don't end up in an endless loop:
491 * The kernel might fire SIGTTOU while an
492 * ioctl() in tcsetattr() is executed. When the ioctl()
493 * is resumed and retries, the signal handler interrupts it again.
494 */
495 signal (SIGTTIN, SIG_IGN);
496 signal (SIGTTOU, SIG_IGN);
497
b5159817 498 newtios = *oldtios;
e0dc0de7 499
a7c97a40
CB
500 /* We use the same settings that ssh does. */
501 newtios.c_iflag |= IGNPAR;
502 newtios.c_iflag &= ~(ISTRIP | INLCR | IGNCR | ICRNL | IXON | IXANY | IXOFF);
503#ifdef IUCLC
504 newtios.c_iflag &= ~IUCLC;
505#endif
4dc96430 506 newtios.c_lflag &= ~(TOSTOP | ISIG | ICANON | ECHO | ECHOE | ECHOK | ECHONL);
a7c97a40
CB
507#ifdef IEXTEN
508 newtios.c_lflag &= ~IEXTEN;
509#endif
90275474 510 newtios.c_oflag |= ONLCR;
300ec717 511 newtios.c_oflag |= OPOST;
b5159817
DE
512 newtios.c_cc[VMIN] = 1;
513 newtios.c_cc[VTIME] = 0;
e0dc0de7 514
a7c97a40 515 /* Set new attributes. */
e4953e62
CB
516 ret = tcsetattr(fd, TCSAFLUSH, &newtios);
517 if (ret < 0) {
518 ERROR("Failed to set new terminal settings");
b5159817 519 return -1;
e0dc0de7
DL
520 }
521
63376d7d 522 return 0;
b5159817 523}
e0dc0de7 524
dcad02f8 525static void lxc_terminal_peer_proxy_free(struct lxc_terminal *terminal)
b5159817 526{
28327a43 527 lxc_terminal_signal_fini(terminal);
e788f4ac 528
36a94ce8
CB
529 close(terminal->proxy.ptx);
530 terminal->proxy.ptx = -1;
e788f4ac 531
41808e20
CB
532 close(terminal->proxy.pty);
533 terminal->proxy.pty = -1;
e788f4ac 534
fb87aa6a 535 terminal->proxy.busy = -1;
e788f4ac 536
fb87aa6a 537 terminal->proxy.name[0] = '\0';
e788f4ac 538
2083d59d 539 terminal->peer = -1;
b5159817 540}
596a818d 541
60dd8ef4
CB
542static int lxc_terminal_peer_proxy_alloc(struct lxc_terminal *terminal,
543 int sockfd)
b5159817 544{
60dd8ef4 545 int ret;
b5159817 546 struct termios oldtermio;
5b55021f 547 struct lxc_terminal_state *ts;
b5159817 548
36a94ce8 549 if (terminal->ptx < 0) {
2083d59d 550 ERROR("Terminal not set up");
b5159817
DE
551 return -1;
552 }
60dd8ef4 553
fb87aa6a 554 if (terminal->proxy.busy != -1 || terminal->peer != -1) {
2083d59d 555 NOTICE("Terminal already in use");
b5159817
DE
556 return -1;
557 }
60dd8ef4 558
2083d59d 559 if (terminal->tty_state) {
60dd8ef4 560 ERROR("Terminal has already been initialized");
b5159817 561 return -1;
596a818d
DE
562 }
563
d712f9e8 564 /* This is the proxy terminal that will be given to the client, and
36a94ce8 565 * that the real terminal ptx will send to / recv from.
b5159817 566 */
41808e20 567 ret = openpty(&terminal->proxy.ptx, &terminal->proxy.pty, NULL,
3f15bdd9 568 NULL, NULL);
60dd8ef4
CB
569 if (ret < 0) {
570 SYSERROR("Failed to open proxy terminal");
b5159817
DE
571 return -1;
572 }
596a818d 573
41808e20 574 ret = ttyname_r(terminal->proxy.pty, terminal->proxy.name,
3f15bdd9
CB
575 sizeof(terminal->proxy.name));
576 if (ret < 0) {
41808e20 577 SYSERROR("Failed to retrieve name of proxy terminal pty");
3f15bdd9
CB
578 goto on_error;
579 }
580
36a94ce8 581 ret = fd_cloexec(terminal->proxy.ptx, true);
408c18a1 582 if (ret < 0) {
36a94ce8 583 SYSERROR("Failed to set FD_CLOEXEC flag on proxy terminal ptx");
408c18a1
CB
584 goto on_error;
585 }
586
41808e20 587 ret = fd_cloexec(terminal->proxy.pty, true);
408c18a1 588 if (ret < 0) {
41808e20 589 SYSERROR("Failed to set FD_CLOEXEC flag on proxy terminal pty");
408c18a1
CB
590 goto on_error;
591 }
592
41808e20 593 ret = lxc_setup_tios(terminal->proxy.pty, &oldtermio);
60dd8ef4
CB
594 if (ret < 0)
595 goto on_error;
b5159817 596
36a94ce8 597 ts = lxc_terminal_signal_init(terminal->proxy.ptx, terminal->ptx);
b5159817 598 if (!ts)
60dd8ef4 599 goto on_error;
b5159817 600
2083d59d 601 terminal->tty_state = ts;
41808e20 602 terminal->peer = terminal->proxy.pty;
fb87aa6a 603 terminal->proxy.busy = sockfd;
2083d59d 604 ret = lxc_terminal_mainloop_add_peer(terminal);
a529bc25 605 if (ret < 0)
60dd8ef4 606 goto on_error;
b5159817 607
41808e20
CB
608 NOTICE("Opened proxy terminal with ptx fd %d and pty fd %d",
609 terminal->proxy.ptx, terminal->proxy.pty);
b5159817
DE
610 return 0;
611
60dd8ef4 612on_error:
2083d59d 613 lxc_terminal_peer_proxy_free(terminal);
63376d7d
DL
614 return -1;
615}
616
c1ee47cd 617int lxc_terminal_allocate(struct lxc_conf *conf, int sockfd, int *ttyreq)
b5159817 618{
8703bf5b 619 size_t ttynum;
36a94ce8 620 int ptxfd = -1;
0e4be3cf 621 struct lxc_tty_info *ttys = &conf->ttys;
dcad02f8 622 struct lxc_terminal *terminal = &conf->console;
b5159817 623
b5159817 624 if (*ttyreq == 0) {
12c2eaaa
CB
625 int ret;
626
627 ret = lxc_terminal_peer_proxy_alloc(terminal, sockfd);
628 if (ret < 0)
b5159817 629 goto out;
12c2eaaa 630
36a94ce8 631 ptxfd = terminal->proxy.ptx;
b5159817
DE
632 goto out;
633 }
634
635 if (*ttyreq > 0) {
8703bf5b 636 if ((size_t)*ttyreq > ttys->max)
b5159817
DE
637 goto out;
638
730aaf46 639 if (ttys->tty[*ttyreq - 1].busy >= 0)
b5159817
DE
640 goto out;
641
12c2eaaa 642 /* The requested tty is available. */
b5159817
DE
643 ttynum = *ttyreq;
644 goto out_tty;
645 }
646
12c2eaaa 647 /* Search for next available tty, fixup index tty1 => [0]. */
730aaf46 648 for (ttynum = 1; ttynum <= ttys->max && ttys->tty[ttynum - 1].busy >= 0; ttynum++) {
0d4137cc 649 ;
12c2eaaa 650 }
b5159817 651
12c2eaaa 652 /* We didn't find any available slot for tty. */
885766f5 653 if (ttynum > ttys->max)
b5159817
DE
654 goto out;
655
8703bf5b 656 *ttyreq = (int)ttynum;
b5159817
DE
657
658out_tty:
0e4be3cf 659 ttys->tty[ttynum - 1].busy = sockfd;
36a94ce8 660 ptxfd = ttys->tty[ttynum - 1].ptx;
12c2eaaa 661
b5159817 662out:
36a94ce8 663 return ptxfd;
b5159817
DE
664}
665
3dfe6f8d 666void lxc_terminal_free(struct lxc_conf *conf, int fd)
63376d7d 667{
0e4be3cf 668 struct lxc_tty_info *ttys = &conf->ttys;
dcad02f8 669 struct lxc_terminal *terminal = &conf->console;
b5159817 670
8703bf5b 671 for (size_t i = 0; i < ttys->max; i++)
0e4be3cf 672 if (ttys->tty[i].busy == fd)
730aaf46 673 ttys->tty[i].busy = -1;
b5159817 674
1b5e93c4
CB
675 if (terminal->proxy.busy != fd)
676 return;
677
41808e20 678 lxc_mainloop_del_handler(terminal->descr, terminal->proxy.pty);
1b5e93c4 679 lxc_terminal_peer_proxy_free(terminal);
b5159817
DE
680}
681
dcad02f8 682static int lxc_terminal_peer_default(struct lxc_terminal *terminal)
b5159817 683{
5b55021f 684 struct lxc_terminal_state *ts;
46768cce 685 const char *path;
467c7ff3 686 int ret = 0;
b5159817 687
46768cce
CB
688 if (terminal->path)
689 path = terminal->path;
690 else
691 path = "/dev/tty";
b5159817 692
2083d59d
CB
693 terminal->peer = lxc_unpriv(open(path, O_RDWR | O_CLOEXEC));
694 if (terminal->peer < 0) {
46768cce
CB
695 if (!terminal->path) {
696 errno = ENODEV;
7874d81a 697 SYSDEBUG("The process does not have a controlling terminal");
46768cce
CB
698 goto on_succes;
699 }
700
6d1400b5 701 SYSERROR("Failed to open proxy terminal \"%s\"", path);
467c7ff3
CB
702 return -ENOTTY;
703 }
49cd0656 704 DEBUG("Using terminal \"%s\" as proxy", path);
b5159817 705
2083d59d 706 if (!isatty(terminal->peer)) {
49cd0656
CB
707 ERROR("File descriptor for \"%s\" does not refer to a terminal", path);
708 goto on_error_free_tios;
467c7ff3 709 }
b5159817 710
36a94ce8 711 ts = lxc_terminal_signal_init(terminal->peer, terminal->ptx);
2083d59d 712 terminal->tty_state = ts;
341c2aed 713 if (!ts) {
0519b5cc 714 WARN("Failed to install signal handler");
49cd0656 715 goto on_error_free_tios;
341c2aed 716 }
b5159817 717
36a94ce8 718 lxc_terminal_winsz(terminal->peer, terminal->ptx);
b5159817 719
2083d59d 720 terminal->tios = malloc(sizeof(*terminal->tios));
49cd0656
CB
721 if (!terminal->tios)
722 goto on_error_free_tios;
b5159817 723
49cd0656
CB
724 ret = lxc_setup_tios(terminal->peer, terminal->tios);
725 if (ret < 0)
726 goto on_error_close_peer;
467c7ff3 727 else
49cd0656 728 goto on_succes;
b5159817 729
49cd0656 730on_error_free_tios:
2083d59d
CB
731 free(terminal->tios);
732 terminal->tios = NULL;
467c7ff3 733
49cd0656 734on_error_close_peer:
2083d59d
CB
735 close(terminal->peer);
736 terminal->peer = -1;
467c7ff3
CB
737 ret = -ENOTTY;
738
49cd0656 739on_succes:
467c7ff3 740 return ret;
b5159817
DE
741}
742
dcad02f8 743int lxc_terminal_write_ringbuffer(struct lxc_terminal *terminal)
39c6cdb7
CB
744{
745 char *r_addr;
746 ssize_t ret;
747 uint64_t used;
2083d59d 748 struct lxc_ringbuf *buf = &terminal->ringbuf;
39c6cdb7
CB
749
750 /* There's not log file where we can dump the ringbuffer to. */
2083d59d 751 if (terminal->log_fd < 0)
39c6cdb7
CB
752 return 0;
753
39c6cdb7
CB
754 used = lxc_ringbuf_used(buf);
755 if (used == 0)
756 return 0;
757
2083d59d 758 ret = lxc_terminal_truncate_log_file(terminal);
39c6cdb7
CB
759 if (ret < 0)
760 return ret;
761
762 /* Write as much as we can without exceeding the limit. */
2083d59d
CB
763 if (terminal->log_size < used)
764 used = terminal->log_size;
39c6cdb7
CB
765
766 r_addr = lxc_ringbuf_get_read_addr(buf);
2083d59d 767 ret = lxc_write_nointr(terminal->log_fd, r_addr, used);
39c6cdb7
CB
768 if (ret < 0)
769 return -EIO;
770
771 return 0;
772}
773
dcad02f8 774void lxc_terminal_delete(struct lxc_terminal *terminal)
b5159817 775{
69629c82
CB
776 int ret;
777
2083d59d 778 ret = lxc_terminal_write_ringbuffer(terminal);
39c6cdb7 779 if (ret < 0)
2083d59d 780 WARN("Failed to write terminal log to disk");
39c6cdb7 781
2083d59d
CB
782 if (terminal->tios && terminal->peer >= 0) {
783 ret = tcsetattr(terminal->peer, TCSAFLUSH, terminal->tios);
69629c82 784 if (ret < 0)
a24c5678 785 SYSWARN("Failed to set old terminal settings");
69629c82 786 }
2083d59d
CB
787 free(terminal->tios);
788 terminal->tios = NULL;
596a818d 789
2083d59d
CB
790 if (terminal->peer >= 0)
791 close(terminal->peer);
792 terminal->peer = -1;
bc9724f7 793
36a94ce8
CB
794 if (terminal->ptx >= 0)
795 close(terminal->ptx);
796 terminal->ptx = -1;
bc9724f7 797
41808e20
CB
798 if (terminal->pty >= 0)
799 close(terminal->pty);
800 terminal->pty = -1;
bc9724f7 801
d926c261
CB
802 terminal->pty_nr = -1;
803
2083d59d
CB
804 if (terminal->log_fd >= 0)
805 close(terminal->log_fd);
806 terminal->log_fd = -1;
63376d7d
DL
807}
808
3b988b33
CB
809/**
810 * Note that this function needs to run before the mainloop starts. Since we
36a94ce8 811 * register a handler for the terminal's ptxfd when we create the mainloop
2083d59d 812 * the terminal handler needs to see an allocated ringbuffer.
3b988b33 813 */
dcad02f8 814static int lxc_terminal_create_ringbuf(struct lxc_terminal *terminal)
3b988b33
CB
815{
816 int ret;
2083d59d
CB
817 struct lxc_ringbuf *buf = &terminal->ringbuf;
818 uint64_t size = terminal->buffer_size;
3b988b33
CB
819
820 /* no ringbuffer previously allocated and no ringbuffer requested */
821 if (!buf->addr && size <= 0)
822 return 0;
823
824 /* ringbuffer allocated but no new ringbuffer requested */
825 if (buf->addr && size <= 0) {
826 lxc_ringbuf_release(buf);
827 buf->addr = NULL;
828 buf->r_off = 0;
829 buf->w_off = 0;
830 buf->size = 0;
2083d59d 831 TRACE("Deallocated terminal ringbuffer");
3b988b33
CB
832 return 0;
833 }
834
835 if (size <= 0)
836 return 0;
837
838 /* check wether the requested size for the ringbuffer has changed */
839 if (buf->addr && buf->size != size) {
2083d59d
CB
840 TRACE("Terminal ringbuffer size changed from %" PRIu64
841 " to %" PRIu64 " bytes. Deallocating terminal ringbuffer",
3b988b33
CB
842 buf->size, size);
843 lxc_ringbuf_release(buf);
844 }
845
846 ret = lxc_ringbuf_create(buf, size);
847 if (ret < 0) {
2083d59d 848 ERROR("Failed to setup %" PRIu64 " byte terminal ringbuffer", size);
3b988b33
CB
849 return -1;
850 }
851
2083d59d 852 TRACE("Allocated %" PRIu64 " byte terminal ringbuffer", size);
3b988b33
CB
853 return 0;
854}
855
a0309168 856/**
2083d59d
CB
857 * This is the terminal log file. Please note that the terminal log file is
858 * (implementation wise not content wise) independent of the terminal ringbuffer.
a0309168 859 */
dcad02f8 860int lxc_terminal_create_log_file(struct lxc_terminal *terminal)
a0309168 861{
2083d59d 862 if (!terminal->log_path)
a0309168
CB
863 return 0;
864
2083d59d
CB
865 terminal->log_fd = lxc_unpriv(open(terminal->log_path, O_CLOEXEC | O_RDWR | O_CREAT | O_APPEND, 0600));
866 if (terminal->log_fd < 0) {
867 SYSERROR("Failed to open terminal log file \"%s\"", terminal->log_path);
a0309168
CB
868 return -1;
869 }
870
2083d59d 871 DEBUG("Using \"%s\" as terminal log file", terminal->log_path);
a0309168
CB
872 return 0;
873}
874
8ea93a0f
CB
875static int lxc_terminal_map_ids(struct lxc_conf *c, struct lxc_terminal *terminal)
876{
877 int ret;
878
0589d744 879 if (list_empty(&c->id_map))
8ea93a0f
CB
880 return 0;
881
882 if (is_empty_string(terminal->name) && terminal->pty < 0)
883 return 0;
884
885 if (terminal->pty >= 0)
886 ret = userns_exec_mapped_root(NULL, terminal->pty, c);
887 else
888 ret = userns_exec_mapped_root(terminal->name, -EBADF, c);
889 if (ret < 0)
890 return log_error(-1, "Failed to chown terminal %d(%s)", terminal->pty,
891 !is_empty_string(terminal->name) ? terminal->name : "(null)");
892
893 TRACE("Chowned terminal %d(%s)", terminal->pty,
894 !is_empty_string(terminal->name) ? terminal->name : "(null)");
895
896 return 0;
897}
898
899static int lxc_terminal_create_foreign(struct lxc_conf *conf, struct lxc_terminal *terminal)
63376d7d 900{
8ded9244 901 int ret;
b5159817 902
41808e20 903 ret = openpty(&terminal->ptx, &terminal->pty, NULL, NULL, NULL);
467c7ff3 904 if (ret < 0) {
8ded9244 905 SYSERROR("Failed to open terminal");
b5159817
DE
906 return -1;
907 }
908
8ea93a0f
CB
909 ret = lxc_terminal_map_ids(conf, terminal);
910 if (ret < 0) {
911 SYSERROR("Failed to change ownership of terminal multiplexer device");
912 goto err;
913 }
914
41808e20 915 ret = ttyname_r(terminal->pty, terminal->name, sizeof(terminal->name));
3f15bdd9 916 if (ret < 0) {
41808e20 917 SYSERROR("Failed to retrieve name of terminal pty");
3f15bdd9
CB
918 goto err;
919 }
920
36a94ce8 921 ret = fd_cloexec(terminal->ptx, true);
69629c82 922 if (ret < 0) {
36a94ce8 923 SYSERROR("Failed to set FD_CLOEXEC flag on terminal ptx");
b5159817
DE
924 goto err;
925 }
926
41808e20 927 ret = fd_cloexec(terminal->pty, true);
69629c82 928 if (ret < 0) {
41808e20 929 SYSERROR("Failed to set FD_CLOEXEC flag on terminal pty");
b5159817
DE
930 goto err;
931 }
932
2083d59d 933 ret = lxc_terminal_peer_default(terminal);
467c7ff3 934 if (ret < 0) {
8ded9244 935 ERROR("Failed to allocate proxy terminal");
467c7ff3
CB
936 goto err;
937 }
b5159817 938
5777fe90
CB
939 return 0;
940
941err:
2083d59d 942 lxc_terminal_delete(terminal);
5777fe90
CB
943 return -ENODEV;
944}
945
18129d94
CB
946int lxc_devpts_terminal(int devpts_fd, int *ret_ptx, int *ret_pty,
947 int *ret_pty_nr, bool require_tiocgptpeer)
f797f05e 948{
803839b8
CB
949 __do_close int fd_devpts = -EBADF, fd_ptx = -EBADF,
950 fd_opath_pty = -EBADF, fd_pty = -EBADF;
cb01e311 951 int pty_nr = -1;
f797f05e
CB
952 int ret;
953
803839b8
CB
954 /*
955 * When we aren't told what devpts instance to allocate from we assume
956 * it is the one in the caller's mount namespace.
957 * This poses a slight complication, a lot of distros will change
958 * permissions on /dev/ptmx so it can be opened by unprivileged users
959 * but will not change permissions on /dev/pts/ptmx itself. In
960 * addition, /dev/ptmx can either be a symlink, a bind-mount, or a
961 * separate device node. So we need to allow for fairly lax lookup.
962 */
9f77617b 963 if (devpts_fd < 0)
803839b8
CB
964 fd_ptx = open_at(-EBADF, "/dev/ptmx", PROTECT_OPEN_RW & ~O_NOFOLLOW,
965 PROTECT_LOOKUP_ABSOLUTE_XDEV_SYMLINKS, 0);
966 else
967 fd_ptx = open_beneath(devpts_fd, "ptmx", O_RDWR | O_NOCTTY | O_CLOEXEC);
4655bdd1 968 if (fd_ptx < 0) {
7a316559
CB
969 if (errno == ENOSPC)
970 return systrace("Exceeded number of allocatable terminals");
971
972 return syserror("Failed to open terminal multiplexer device");
973 }
f797f05e 974
803839b8
CB
975 if (devpts_fd < 0) {
976 fd_devpts = open_at(-EBADF, "/dev/pts", PROTECT_OPATH_DIRECTORY,
977 PROTECT_LOOKUP_ABSOLUTE_XDEV, 0);
978 if (fd_devpts < 0)
979 return syserror("Failed to open devpts instance");
980
981 if (!same_device(fd_devpts, "ptmx", fd_ptx, ""))
982 return syserror("The acquired ptmx devices don't match");
983 devpts_fd = fd_devpts;
984 }
985
4655bdd1
CB
986 ret = unlockpt(fd_ptx);
987 if (ret < 0)
988 return syswarn_set(-ENODEV, "Failed to unlock multiplexer device device");
f797f05e 989
4655bdd1
CB
990 fd_pty = ioctl(fd_ptx, TIOCGPTPEER, O_RDWR | O_NOCTTY | O_CLOEXEC);
991 if (fd_pty < 0) {
f382bcc6
CB
992 switch (errno) {
993 case ENOTTY:
994 SYSTRACE("Pure fd-based terminal allocation not possible");
995 break;
996 case ENOSPC:
7a316559 997 SYSTRACE("Exceeded number of allocatable terminals");
f382bcc6
CB
998 break;
999 default:
1000 SYSWARN("Failed to allocate new pty device");
64ac925f 1001 return -errno;
f382bcc6 1002 }
f797f05e 1003
64ac925f 1004 /* The caller tells us that they trust the devpts instance. */
18129d94
CB
1005 if (require_tiocgptpeer)
1006 return ret_errno(ENODEV);
8ea93a0f 1007 }
f797f05e 1008
4655bdd1
CB
1009 ret = ioctl(fd_ptx, TIOCGPTN, &pty_nr);
1010 if (ret)
1011 return syswarn_set(-ENODEV, "Failed to retrieve name of terminal pty");
cb01e311 1012
18129d94
CB
1013 if (fd_pty < 0) {
1014 /*
1015 * If we end up it means that TIOCGPTPEER isn't supported but
1016 * the caller told us they trust the devpts instance so we use
1017 * the pty nr to open the pty side.
1018 */
1019 fd_pty = open_at(devpts_fd, fdstr(pty_nr), PROTECT_OPEN_RW,
1020 PROTECT_LOOKUP_ABSOLUTE_XDEV, 0);
1021 if (fd_pty < 0)
1022 return syswarn_set(-ENODEV, "Failed to open terminal pty fd by path %d/%d",
1023 devpts_fd, pty_nr);
1024 } else {
1025 fd_opath_pty = open_at(devpts_fd, fdstr(pty_nr), PROTECT_OPATH_FILE,
1026 PROTECT_LOOKUP_ABSOLUTE_XDEV, 0);
1027 if (fd_opath_pty < 0)
1028 return syswarn_set(-ENODEV, "Failed to open terminal pty fd by path %d/%d",
1029 devpts_fd, pty_nr);
1030
1031 if (!same_file_lax(fd_pty, fd_opath_pty))
1032 return syswarn_set(-ENODEV, "Terminal file descriptor changed");
1033 }
4655bdd1 1034
4655bdd1
CB
1035 *ret_ptx = move_fd(fd_ptx);
1036 *ret_pty = move_fd(fd_pty);
d926c261 1037 *ret_pty_nr = pty_nr;
4655bdd1
CB
1038 return 0;
1039}
1040
9f77617b
CB
1041int lxc_terminal_parent(struct lxc_conf *conf)
1042{
9f77617b
CB
1043 struct lxc_terminal *console = &conf->console;
1044 int ret;
1045
1046 if (!wants_console(&conf->console))
1047 return 0;
1048
1049 /* Allocate console from the container's devpts. */
1050 if (conf->pty_max > 1)
1051 return 0;
1052
1053 /* Allocate console for the container from the host's devpts. */
803839b8 1054 ret = lxc_devpts_terminal(-EBADF, &console->ptx, &console->pty,
18129d94 1055 &console->pty_nr, false);
9f77617b
CB
1056 if (ret < 0)
1057 return syserror("Failed to allocate console");
1058
1059 ret = strnprintf(console->name, sizeof(console->name),
1060 "/dev/pts/%d", console->pty_nr);
1061 if (ret < 0)
1062 return syserror("Failed to create console path");
1063
1064 return lxc_terminal_map_ids(conf, &conf->console);
1065}
1066
4655bdd1 1067static int lxc_terminal_create_native(const char *name, const char *lxcpath,
4655bdd1
CB
1068 struct lxc_terminal *terminal)
1069{
1070 __do_close int devpts_fd = -EBADF;
1071 int ret;
1072
1073 devpts_fd = lxc_cmd_get_devpts_fd(name, lxcpath);
1074 if (devpts_fd < 0)
23cc33cd 1075 return sysinfo("Failed to receive devpts fd");
4655bdd1 1076
4dcf0c43 1077 ret = lxc_devpts_terminal(devpts_fd, &terminal->ptx, &terminal->pty,
18129d94 1078 &terminal->pty_nr, true);
4655bdd1
CB
1079 if (ret < 0)
1080 return ret;
cb01e311 1081
d926c261
CB
1082 ret = strnprintf(terminal->name, sizeof(terminal->name),
1083 "/dev/pts/%d", terminal->pty_nr);
1084 if (ret < 0)
1085 return syserror("Failed to create path");
1086
f797f05e
CB
1087 ret = lxc_terminal_peer_default(terminal);
1088 if (ret < 0) {
4655bdd1
CB
1089 lxc_terminal_delete(terminal);
1090 return syswarn_set(-ENODEV, "Failed to allocate proxy terminal");
f797f05e
CB
1091 }
1092
1093 return 0;
f797f05e
CB
1094}
1095
550ede0a
CB
1096int lxc_terminal_create(const char *name, const char *lxcpath,
1097 struct lxc_conf *conf, struct lxc_terminal *terminal)
f797f05e 1098{
4dcf0c43 1099 if (!lxc_terminal_create_native(name, lxcpath, terminal))
f797f05e
CB
1100 return 0;
1101
8ea93a0f 1102 return lxc_terminal_create_foreign(conf, terminal);
f797f05e
CB
1103}
1104
564e31c4 1105int lxc_terminal_setup(struct lxc_conf *conf)
5777fe90
CB
1106{
1107 int ret;
dcad02f8 1108 struct lxc_terminal *terminal = &conf->console;
5777fe90 1109
5ef86378
CB
1110 if (terminal->path && strequal(terminal->path, "none"))
1111 return log_info(0, "No terminal requested");
5777fe90 1112
9f77617b 1113 ret = lxc_terminal_peer_default(terminal);
5777fe90 1114 if (ret < 0)
9f77617b 1115 goto err;
5777fe90 1116
2083d59d 1117 ret = lxc_terminal_create_log_file(terminal);
a0309168
CB
1118 if (ret < 0)
1119 goto err;
1120
2083d59d 1121 ret = lxc_terminal_create_ringbuf(terminal);
a0309168
CB
1122 if (ret < 0)
1123 goto err;
b5159817
DE
1124
1125 return 0;
1126
1127err:
2083d59d 1128 lxc_terminal_delete(terminal);
69629c82 1129 return -ENODEV;
b5159817
DE
1130}
1131
8ca7b374
CB
1132static bool __terminal_dup2(int duplicate, int original)
1133{
1134 int ret;
1135
1136 if (!isatty(original))
1137 return true;
1138
1139 ret = dup2(duplicate, original);
1140 if (ret < 0) {
1141 SYSERROR("Failed to dup2(%d, %d)", duplicate, original);
1142 return false;
1143 }
1144
1145 return true;
1146}
1147
ae6d3913 1148int lxc_terminal_set_stdfds(int fd)
0d9acb99 1149{
8ca7b374
CB
1150 int i;
1151
39a78bbe 1152 if (fd < 0)
0d9acb99 1153 return 0;
b5159817 1154
8ca7b374
CB
1155 for (i = 0; i < 3; i++)
1156 if (!__terminal_dup2(fd, (int[]){STDIN_FILENO, STDOUT_FILENO,
1157 STDERR_FILENO}[i]))
39a78bbe 1158 return -1;
39a78bbe 1159
0d9acb99
DE
1160 return 0;
1161}
b5159817 1162
52f9292f 1163int lxc_terminal_stdin_cb(int fd, uint32_t events, void *cbdata,
3298b37d 1164 struct lxc_async_descr *descr)
b5159817 1165{
15085292 1166 int ret;
b5159817 1167 char c;
15085292 1168 struct lxc_terminal_state *ts = cbdata;
b5159817 1169
97bc2422 1170 if (fd != ts->stdinfd)
a529bc25 1171 return LXC_MAINLOOP_CLOSE;
97bc2422 1172
15085292
CB
1173 ret = lxc_read_nointr(ts->stdinfd, &c, 1);
1174 if (ret <= 0)
a529bc25 1175 return LXC_MAINLOOP_CLOSE;
63376d7d 1176
525e2117 1177 if (ts->escape >= 1) {
2083d59d 1178 /* we want to exit the terminal with Ctrl+a q */
014d5e1e
CB
1179 if (c == ts->escape && !ts->saw_escape) {
1180 ts->saw_escape = 1;
15085292 1181 return LXC_MAINLOOP_CONTINUE;
014d5e1e 1182 }
5c294060 1183
014d5e1e 1184 if (c == 'q' && ts->saw_escape)
a529bc25 1185 return LXC_MAINLOOP_CLOSE;
014d5e1e
CB
1186
1187 ts->saw_escape = 0;
1188 }
63376d7d 1189
36a94ce8 1190 ret = lxc_write_nointr(ts->ptxfd, &c, 1);
15085292 1191 if (ret <= 0)
a529bc25 1192 return LXC_MAINLOOP_CLOSE;
b5159817 1193
15085292 1194 return LXC_MAINLOOP_CONTINUE;
63376d7d
DL
1195}
1196
36a94ce8 1197int lxc_terminal_ptx_cb(int fd, uint32_t events, void *cbdata,
3298b37d 1198 struct lxc_async_descr *descr)
63376d7d 1199{
0d4137cc 1200 int r, w;
5bd171bd
CB
1201 char buf[LXC_TERMINAL_BUFFER_SIZE];
1202 struct lxc_terminal_state *ts = cbdata;
63376d7d 1203
36a94ce8 1204 if (fd != ts->ptxfd)
a529bc25 1205 return LXC_MAINLOOP_CLOSE;
97bc2422 1206
e66b6c96
CB
1207 r = lxc_read_nointr(fd, buf, sizeof(buf));
1208 if (r <= 0)
a529bc25 1209 return LXC_MAINLOOP_CLOSE;
1560f6c9 1210
e66b6c96 1211 w = lxc_write_nointr(ts->stdoutfd, buf, r);
5bd171bd 1212 if (w <= 0 || w != r)
a529bc25 1213 return LXC_MAINLOOP_CLOSE;
f78a1f32 1214
5bd171bd 1215 return LXC_MAINLOOP_CONTINUE;
b5159817
DE
1216}
1217
36a94ce8 1218int lxc_terminal_getfd(struct lxc_container *c, int *ttynum, int *ptxfd)
b5159817 1219{
7e85a2c4 1220 return lxc_cmd_get_tty_fd(c->name, ttynum, ptxfd, c->config_path);
b5159817
DE
1221}
1222
1223int lxc_console(struct lxc_container *c, int ttynum,
1224 int stdinfd, int stdoutfd, int stderrfd,
1225 int escape)
1226{
36a94ce8 1227 int ptxfd, ret, ttyfd;
3298b37d 1228 struct lxc_async_descr descr;
b5159817 1229 struct termios oldtios;
5b55021f 1230 struct lxc_terminal_state *ts;
28327a43
CB
1231 struct lxc_terminal terminal = {
1232 .tty_state = NULL,
1233 };
25964232 1234 int istty = 0;
b5159817 1235
7e85a2c4 1236 ttyfd = lxc_cmd_get_tty_fd(c->name, &ttynum, &ptxfd, c->config_path);
6834f805
CB
1237 if (ttyfd < 0)
1238 return -1;
b5159817
DE
1239
1240 ret = setsid();
33b4b411
CB
1241 if (ret < 0)
1242 TRACE("Process is already group leader");
b5159817 1243
36a94ce8 1244 ts = lxc_terminal_signal_init(stdinfd, ptxfd);
b5159817
DE
1245 if (!ts) {
1246 ret = -1;
33b4b411 1247 goto close_fds;
b5159817 1248 }
28327a43 1249 terminal.tty_state = ts;
b5159817 1250 ts->escape = escape;
3b975060 1251 ts->stdoutfd = stdoutfd;
b5159817 1252
6834f805 1253 istty = isatty(stdinfd);
25964232 1254 if (istty) {
36a94ce8
CB
1255 lxc_terminal_winsz(stdinfd, ptxfd);
1256 lxc_terminal_winsz(ts->stdinfd, ts->ptxfd);
6834f805 1257 } else {
71ac3f07 1258 INFO("File descriptor %d does not refer to a terminal", stdinfd);
25964232 1259 }
b5159817
DE
1260
1261 ret = lxc_mainloop_open(&descr);
1262 if (ret) {
33b4b411
CB
1263 ERROR("Failed to create mainloop");
1264 goto sigwinch_fini;
b5159817
DE
1265 }
1266
341c2aed
CB
1267 if (ts->sigfd != -1) {
1268 ret = lxc_mainloop_add_handler(&descr, ts->sigfd,
543d2f83
CB
1269 lxc_terminal_signalfd_cb,
1270 default_cleanup_handler,
1271 ts, "lxc_terminal_signalfd_cb");
33b4b411 1272 if (ret < 0) {
0519b5cc 1273 ERROR("Failed to add signal handler to mainloop");
33b4b411 1274 goto close_mainloop;
341c2aed 1275 }
b5159817
DE
1276 }
1277
1278 ret = lxc_mainloop_add_handler(&descr, ts->stdinfd,
543d2f83
CB
1279 lxc_terminal_stdin_cb,
1280 default_cleanup_handler,
1281 ts, "lxc_terminal_stdin_cb");
33b4b411
CB
1282 if (ret < 0) {
1283 ERROR("Failed to add stdin handler");
1284 goto close_mainloop;
b5159817
DE
1285 }
1286
36a94ce8 1287 ret = lxc_mainloop_add_handler(&descr, ts->ptxfd,
543d2f83
CB
1288 lxc_terminal_ptx_cb,
1289 default_cleanup_handler,
1290 ts, "lxc_terminal_ptx_cb");
33b4b411 1291 if (ret < 0) {
36a94ce8 1292 ERROR("Failed to add ptx handler");
33b4b411 1293 goto close_mainloop;
b5159817
DE
1294 }
1295
686df166
CB
1296 if (ts->escape >= 1) {
1297 fprintf(stderr,
1298 "\n"
6834f805
CB
1299 "Connected to tty %1$d\n"
1300 "Type <Ctrl+%2$c q> to exit the console, "
1301 "<Ctrl+%2$c Ctrl+%2$c> to enter Ctrl+%2$c itself\n",
1302 ttynum, 'a' + escape - 1);
686df166 1303 }
6834f805
CB
1304
1305 if (istty) {
1306 ret = lxc_setup_tios(stdinfd, &oldtios);
1307 if (ret < 0)
1308 goto close_mainloop;
1309 }
1310
025ed0f3 1311 ret = lxc_mainloop(&descr, -1);
33b4b411
CB
1312 if (ret < 0) {
1313 ERROR("The mainloop returned an error");
6834f805 1314 goto restore_tios;
b5159817
DE
1315 }
1316
1317 ret = 0;
1318
6834f805
CB
1319restore_tios:
1320 if (istty) {
1321 istty = tcsetattr(stdinfd, TCSAFLUSH, &oldtios);
1322 if (istty < 0)
a24c5678 1323 SYSWARN("Failed to restore terminal properties");
6834f805
CB
1324 }
1325
33b4b411 1326close_mainloop:
b5159817 1327 lxc_mainloop_close(&descr);
33b4b411
CB
1328
1329sigwinch_fini:
28327a43 1330 lxc_terminal_signal_fini(&terminal);
33b4b411
CB
1331
1332close_fds:
36a94ce8 1333 close(ptxfd);
b5159817 1334 close(ttyfd);
33b4b411 1335
b5159817 1336 return ret;
63376d7d 1337}
e98affda 1338
cd0a2b2f 1339int lxc_make_controlling_terminal(int fd)
e98affda
CB
1340{
1341 int ret;
1342
1343 setsid();
1344
1345 ret = ioctl(fd, TIOCSCTTY, (char *)NULL);
1346 if (ret < 0)
1347 return -1;
1348
1349 return 0;
1350}
1351
d049f0e9 1352int lxc_terminal_prepare_login(int fd)
e98affda
CB
1353{
1354 int ret;
1355
cd0a2b2f 1356 ret = lxc_make_controlling_terminal(fd);
e98affda
CB
1357 if (ret < 0)
1358 return -1;
1359
ae6d3913 1360 ret = lxc_terminal_set_stdfds(fd);
e98affda
CB
1361 if (ret < 0)
1362 return -1;
1363
1364 if (fd > STDERR_FILENO)
1365 close(fd);
1366
1367 return 0;
1368}
1369
e9a55b51 1370void lxc_terminal_info_init(struct lxc_terminal_info *terminal)
e98affda 1371{
e9a55b51 1372 terminal->name[0] = '\0';
36a94ce8 1373 terminal->ptx = -EBADF;
41808e20 1374 terminal->pty = -EBADF;
e9a55b51 1375 terminal->busy = -1;
e98affda
CB
1376}
1377
e9a55b51 1378void lxc_terminal_init(struct lxc_terminal *terminal)
e98affda 1379{
e9a55b51 1380 memset(terminal, 0, sizeof(*terminal));
d926c261 1381 terminal->pty_nr = -1;
41808e20 1382 terminal->pty = -EBADF;
36a94ce8 1383 terminal->ptx = -EBADF;
e9a55b51
CB
1384 terminal->peer = -EBADF;
1385 terminal->log_fd = -EBADF;
1386 lxc_terminal_info_init(&terminal->proxy);
e98affda
CB
1387}
1388
dcad02f8 1389void lxc_terminal_conf_free(struct lxc_terminal *terminal)
e98affda 1390{
2083d59d
CB
1391 free(terminal->log_path);
1392 free(terminal->path);
1393 if (terminal->buffer_size > 0 && terminal->ringbuf.addr)
1394 lxc_ringbuf_release(&terminal->ringbuf);
28327a43 1395 lxc_terminal_signal_fini(terminal);
e98affda 1396}