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