Unverified Commit ff305221 by Serge Hallyn Committed by GitHub

Merge pull request #2196 from brauner/2018-02-28/more_terminal_fixes

terminal: improvements and cleanup
parents 19462d43 e9a55b51
...@@ -846,7 +846,7 @@ static int attach_child_main(struct attach_clone_payload *payload) ...@@ -846,7 +846,7 @@ static int attach_child_main(struct attach_clone_payload *payload)
} }
if (options->stdin_fd > 0 && isatty(options->stdin_fd)) { if (options->stdin_fd > 0 && isatty(options->stdin_fd)) {
ret = lxc_make_controlling_pty(options->stdin_fd); ret = lxc_make_controlling_terminal(options->stdin_fd);
if (ret < 0) if (ret < 0)
goto on_error; goto on_error;
} }
...@@ -958,7 +958,7 @@ static int attach_child_main(struct attach_clone_payload *payload) ...@@ -958,7 +958,7 @@ static int attach_child_main(struct attach_clone_payload *payload)
} }
if (options->attach_flags & LXC_ATTACH_ALLOCATE_PTY) { if (options->attach_flags & LXC_ATTACH_ALLOCATE_PTY) {
ret = lxc_login_pty(payload->pty_fd); ret = lxc_terminal_prepare_login(payload->pty_fd);
if (ret < 0) { if (ret < 0) {
SYSERROR("Failed to prepare pty file descriptor %d", payload->pty_fd); SYSERROR("Failed to prepare pty file descriptor %d", payload->pty_fd);
goto on_error; goto on_error;
......
...@@ -27,6 +27,7 @@ ...@@ -27,6 +27,7 @@
#include <stdint.h> #include <stdint.h>
#include "list.h" #include "list.h"
#define LXC_MAINLOOP_ERROR -1
#define LXC_MAINLOOP_CONTINUE 0 #define LXC_MAINLOOP_CONTINUE 0
#define LXC_MAINLOOP_CLOSE 1 #define LXC_MAINLOOP_CLOSE 1
......
...@@ -23,15 +23,14 @@ ...@@ -23,15 +23,14 @@
#include <errno.h> #include <errno.h>
#include <fcntl.h> #include <fcntl.h>
#include <lxc/lxccontainer.h>
#include <signal.h> #include <signal.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <termios.h>
#include <unistd.h>
#include <sys/epoll.h> #include <sys/epoll.h>
#include <sys/types.h> #include <sys/types.h>
#include <termios.h>
#include <lxc/lxccontainer.h> #include <unistd.h>
#include "af_unix.h" #include "af_unix.h"
#include "caps.h" #include "caps.h"
...@@ -41,7 +40,7 @@ ...@@ -41,7 +40,7 @@
#include "log.h" #include "log.h"
#include "lxclock.h" #include "lxclock.h"
#include "mainloop.h" #include "mainloop.h"
#include "start.h" /* for struct lxc_handler */ #include "start.h"
#include "terminal.h" #include "terminal.h"
#include "utils.h" #include "utils.h"
...@@ -117,7 +116,7 @@ int lxc_terminal_signalfd_cb(int fd, uint32_t events, void *cbdata, ...@@ -117,7 +116,7 @@ int lxc_terminal_signalfd_cb(int fd, uint32_t events, void *cbdata,
ret = read(fd, &siginfo, sizeof(siginfo)); ret = read(fd, &siginfo, sizeof(siginfo));
if (ret < 0 || (size_t)ret < sizeof(siginfo)) { if (ret < 0 || (size_t)ret < sizeof(siginfo)) {
ERROR("Failed to read signal info"); ERROR("Failed to read signal info");
return -1; return LXC_MAINLOOP_ERROR;
} }
if (siginfo.ssi_signo == SIGTERM) { if (siginfo.ssi_signo == SIGTERM) {
...@@ -128,13 +127,13 @@ int lxc_terminal_signalfd_cb(int fd, uint32_t events, void *cbdata, ...@@ -128,13 +127,13 @@ int lxc_terminal_signalfd_cb(int fd, uint32_t events, void *cbdata,
if (siginfo.ssi_signo == SIGWINCH) if (siginfo.ssi_signo == SIGWINCH)
lxc_terminal_winch(ts); lxc_terminal_winch(ts);
return 0; return LXC_MAINLOOP_CONTINUE;
} }
struct lxc_terminal_state *lxc_terminal_signal_init(int srcfd, int dstfd) struct lxc_terminal_state *lxc_terminal_signal_init(int srcfd, int dstfd)
{ {
int ret; int ret;
bool istty; bool istty = false;
sigset_t mask; sigset_t mask;
struct lxc_terminal_state *ts; struct lxc_terminal_state *ts;
...@@ -147,20 +146,31 @@ struct lxc_terminal_state *lxc_terminal_signal_init(int srcfd, int dstfd) ...@@ -147,20 +146,31 @@ struct lxc_terminal_state *lxc_terminal_signal_init(int srcfd, int dstfd)
ts->masterfd = dstfd; ts->masterfd = dstfd;
ts->sigfd = -1; ts->sigfd = -1;
sigemptyset(&mask); ret = sigemptyset(&mask);
if (ret < 0) {
SYSERROR("Failed to initialize an empty signal set");
goto on_error;
}
istty = isatty(srcfd) == 1; istty = (isatty(srcfd) == 1);
if (!istty) { if (!istty) {
INFO("fd %d does not refer to a tty device", srcfd); INFO("fd %d does not refer to a tty device", srcfd);
} else { } else {
/* Add tty to list to be scanned at SIGWINCH time. */ /* Add tty to list to be scanned at SIGWINCH time. */
lxc_list_add_elem(&ts->node, ts); lxc_list_add_elem(&ts->node, ts);
lxc_list_add_tail(&lxc_ttys, &ts->node); lxc_list_add_tail(&lxc_ttys, &ts->node);
sigaddset(&mask, SIGWINCH); ret = sigaddset(&mask, SIGWINCH);
if (ret < 0)
NOTICE("%s - Failed to add SIGWINCH to signal set",
strerror(errno));
} }
/* Exit the mainloop cleanly on SIGTERM. */ /* Exit the mainloop cleanly on SIGTERM. */
sigaddset(&mask, SIGTERM); ret = sigaddset(&mask, SIGTERM);
if (ret < 0) {
SYSERROR("Failed to add SIGWINCH to signal set");
goto on_error;
}
ret = sigprocmask(SIG_BLOCK, &mask, &ts->oldmask); ret = sigprocmask(SIG_BLOCK, &mask, &ts->oldmask);
if (ret < 0) { if (ret < 0) {
...@@ -184,8 +194,10 @@ on_error: ...@@ -184,8 +194,10 @@ on_error:
close(ts->sigfd); close(ts->sigfd);
ts->sigfd = -1; ts->sigfd = -1;
} }
if (istty) if (istty)
lxc_list_del(&ts->node); lxc_list_del(&ts->node);
return ts; return ts;
} }
...@@ -243,11 +255,11 @@ static int lxc_terminal_rotate_log_file(struct lxc_terminal *terminal) ...@@ -243,11 +255,11 @@ static int lxc_terminal_rotate_log_file(struct lxc_terminal *terminal)
} }
static int lxc_terminal_write_log_file(struct lxc_terminal *terminal, char *buf, static int lxc_terminal_write_log_file(struct lxc_terminal *terminal, char *buf,
int bytes_read) int bytes_read)
{ {
int ret; int ret;
int64_t space_left = -1;
struct stat st; struct stat st;
int64_t space_left = -1;
if (terminal->log_fd < 0) if (terminal->log_fd < 0)
return 0; return 0;
...@@ -404,7 +416,7 @@ int lxc_terminal_io_cb(int fd, uint32_t events, void *data, ...@@ -404,7 +416,7 @@ int lxc_terminal_io_cb(int fd, uint32_t events, void *data,
if (w_log < 0) if (w_log < 0)
TRACE("Failed to write %d bytes to terminal log", r); TRACE("Failed to write %d bytes to terminal log", r);
return 0; return LXC_MAINLOOP_CONTINUE;
} }
static int lxc_terminal_mainloop_add_peer(struct lxc_terminal *terminal) static int lxc_terminal_mainloop_add_peer(struct lxc_terminal *terminal)
...@@ -434,7 +446,7 @@ static int lxc_terminal_mainloop_add_peer(struct lxc_terminal *terminal) ...@@ -434,7 +446,7 @@ static int lxc_terminal_mainloop_add_peer(struct lxc_terminal *terminal)
} }
int lxc_terminal_mainloop_add(struct lxc_epoll_descr *descr, int lxc_terminal_mainloop_add(struct lxc_epoll_descr *descr,
struct lxc_terminal *terminal) struct lxc_terminal *terminal)
{ {
int ret; int ret;
...@@ -446,7 +458,8 @@ int lxc_terminal_mainloop_add(struct lxc_epoll_descr *descr, ...@@ -446,7 +458,8 @@ int lxc_terminal_mainloop_add(struct lxc_epoll_descr *descr,
ret = lxc_mainloop_add_handler(descr, terminal->master, ret = lxc_mainloop_add_handler(descr, terminal->master,
lxc_terminal_io_cb, terminal); lxc_terminal_io_cb, terminal);
if (ret < 0) { if (ret < 0) {
ERROR("Failed to add handler for %d to mainloop", terminal->master); ERROR("Failed to add handler for terminal master fd %d to "
"mainloop", terminal->master);
return -1; return -1;
} }
...@@ -454,25 +467,24 @@ int lxc_terminal_mainloop_add(struct lxc_epoll_descr *descr, ...@@ -454,25 +467,24 @@ int lxc_terminal_mainloop_add(struct lxc_epoll_descr *descr,
* does attach to it in lxc_terminal_allocate(). * does attach to it in lxc_terminal_allocate().
*/ */
terminal->descr = descr; terminal->descr = descr;
ret = lxc_terminal_mainloop_add_peer(terminal);
if (ret < 0)
return -1;
return 0; return lxc_terminal_mainloop_add_peer(terminal);
} }
int lxc_setup_tios(int fd, struct termios *oldtios) int lxc_setup_tios(int fd, struct termios *oldtios)
{ {
int ret;
struct termios newtios; struct termios newtios;
if (!isatty(fd)) { if (!isatty(fd)) {
ERROR("'%d' is not a tty", fd); ERROR("File descriptor %d does not refert to a terminal", fd);
return -1; return -1;
} }
/* Get current termios */ /* Get current termios. */
if (tcgetattr(fd, oldtios)) { ret = tcgetattr(fd, oldtios);
SYSERROR("failed to get current terminal settings"); if (ret < 0) {
SYSERROR("Failed to get current terminal settings");
return -1; return -1;
} }
...@@ -501,8 +513,9 @@ int lxc_setup_tios(int fd, struct termios *oldtios) ...@@ -501,8 +513,9 @@ int lxc_setup_tios(int fd, struct termios *oldtios)
newtios.c_cc[VTIME] = 0; newtios.c_cc[VTIME] = 0;
/* Set new attributes. */ /* Set new attributes. */
if (tcsetattr(fd, TCSAFLUSH, &newtios)) { ret = tcsetattr(fd, TCSAFLUSH, &newtios);
ERROR("failed to set new terminal settings"); if (ret < 0) {
ERROR("Failed to set new terminal settings");
return -1; return -1;
} }
...@@ -515,75 +528,90 @@ static void lxc_terminal_peer_proxy_free(struct lxc_terminal *terminal) ...@@ -515,75 +528,90 @@ static void lxc_terminal_peer_proxy_free(struct lxc_terminal *terminal)
lxc_terminal_signal_fini(terminal->tty_state); lxc_terminal_signal_fini(terminal->tty_state);
terminal->tty_state = NULL; terminal->tty_state = NULL;
} }
close(terminal->proxy.master); close(terminal->proxy.master);
close(terminal->proxy.slave);
terminal->proxy.master = -1; terminal->proxy.master = -1;
close(terminal->proxy.slave);
terminal->proxy.slave = -1; terminal->proxy.slave = -1;
terminal->proxy.busy = -1; terminal->proxy.busy = -1;
terminal->proxy.name[0] = '\0'; terminal->proxy.name[0] = '\0';
terminal->peer = -1; terminal->peer = -1;
} }
static int lxc_terminal_peer_proxy_alloc(struct lxc_terminal *terminal, int sockfd) static int lxc_terminal_peer_proxy_alloc(struct lxc_terminal *terminal,
int sockfd)
{ {
int ret;
struct termios oldtermio; struct termios oldtermio;
struct lxc_terminal_state *ts; struct lxc_terminal_state *ts;
int ret;
if (terminal->master < 0) { if (terminal->master < 0) {
ERROR("Terminal not set up"); ERROR("Terminal not set up");
return -1; return -1;
} }
if (terminal->proxy.busy != -1 || terminal->peer != -1) { if (terminal->proxy.busy != -1 || terminal->peer != -1) {
NOTICE("Terminal already in use"); NOTICE("Terminal already in use");
return -1; return -1;
} }
if (terminal->tty_state) { if (terminal->tty_state) {
ERROR("Terminal already has tty_state"); ERROR("Terminal has already been initialized");
return -1; return -1;
} }
/* this is the proxy pty that will be given to the client, and that /* This is the proxy terminal that will be given to the client, and
* the real pty master will send to / recv from * that the real terminal master will send to / recv from.
*/ */
ret = openpty(&terminal->proxy.master, &terminal->proxy.slave, ret = openpty(&terminal->proxy.master, &terminal->proxy.slave,
terminal->proxy.name, NULL, NULL); terminal->proxy.name, NULL, NULL);
if (ret) { if (ret < 0) {
SYSERROR("failed to create proxy pty"); SYSERROR("Failed to open proxy terminal");
return -1; return -1;
} }
if (lxc_setup_tios(terminal->proxy.slave, &oldtermio) < 0) ret = lxc_setup_tios(terminal->proxy.slave, &oldtermio);
goto err1; if (ret < 0)
goto on_error;
ts = lxc_terminal_signal_init(terminal->proxy.master, terminal->master); ts = lxc_terminal_signal_init(terminal->proxy.master, terminal->master);
if (!ts) if (!ts)
goto err1; goto on_error;
terminal->tty_state = ts; terminal->tty_state = ts;
terminal->peer = terminal->proxy.slave; terminal->peer = terminal->proxy.slave;
terminal->proxy.busy = sockfd; terminal->proxy.busy = sockfd;
ret = lxc_terminal_mainloop_add_peer(terminal); ret = lxc_terminal_mainloop_add_peer(terminal);
if (ret < 0) if (ret < 0)
goto err1; goto on_error;
DEBUG("%d peermaster:%d sockfd:%d", lxc_raw_getpid(), terminal->proxy.master, sockfd); NOTICE("Opened proxy terminal with master fd %d and slave fd %d",
terminal->proxy.master, terminal->proxy.slave);
return 0; return 0;
err1: on_error:
lxc_terminal_peer_proxy_free(terminal); lxc_terminal_peer_proxy_free(terminal);
return -1; return -1;
} }
int lxc_terminal_allocate(struct lxc_conf *conf, int sockfd, int *ttyreq) int lxc_terminal_allocate(struct lxc_conf *conf, int sockfd, int *ttyreq)
{ {
int masterfd = -1, ttynum; int ttynum;
int masterfd = -1;
struct lxc_tty_info *ttys = &conf->ttys; struct lxc_tty_info *ttys = &conf->ttys;
struct lxc_terminal *terminal = &conf->console; struct lxc_terminal *terminal = &conf->console;
if (*ttyreq == 0) { if (*ttyreq == 0) {
if (lxc_terminal_peer_proxy_alloc(terminal, sockfd) < 0) int ret;
ret = lxc_terminal_peer_proxy_alloc(terminal, sockfd);
if (ret < 0)
goto out; goto out;
masterfd = terminal->proxy.master; masterfd = terminal->proxy.master;
goto out; goto out;
} }
...@@ -595,16 +623,17 @@ int lxc_terminal_allocate(struct lxc_conf *conf, int sockfd, int *ttyreq) ...@@ -595,16 +623,17 @@ int lxc_terminal_allocate(struct lxc_conf *conf, int sockfd, int *ttyreq)
if (ttys->tty[*ttyreq - 1].busy) if (ttys->tty[*ttyreq - 1].busy)
goto out; goto out;
/* the requested tty is available */ /* The requested tty is available. */
ttynum = *ttyreq; ttynum = *ttyreq;
goto out_tty; goto out_tty;
} }
/* search for next available tty, fixup index tty1 => [0] */ /* Search for next available tty, fixup index tty1 => [0]. */
for (ttynum = 1; ttynum <= ttys->nbtty && ttys->tty[ttynum - 1].busy; ttynum++) for (ttynum = 1; ttynum <= ttys->nbtty && ttys->tty[ttynum - 1].busy; ttynum++) {
; ;
}
/* we didn't find any available slot for tty */ /* We didn't find any available slot for tty. */
if (ttynum > ttys->nbtty) if (ttynum > ttys->nbtty)
goto out; goto out;
...@@ -613,6 +642,7 @@ int lxc_terminal_allocate(struct lxc_conf *conf, int sockfd, int *ttyreq) ...@@ -613,6 +642,7 @@ int lxc_terminal_allocate(struct lxc_conf *conf, int sockfd, int *ttyreq)
out_tty: out_tty:
ttys->tty[ttynum - 1].busy = sockfd; ttys->tty[ttynum - 1].busy = sockfd;
masterfd = ttys->tty[ttynum - 1].master; masterfd = ttys->tty[ttynum - 1].master;
out: out:
return masterfd; return masterfd;
} }
...@@ -623,15 +653,15 @@ void lxc_terminal_free(struct lxc_conf *conf, int fd) ...@@ -623,15 +653,15 @@ void lxc_terminal_free(struct lxc_conf *conf, int fd)
struct lxc_tty_info *ttys = &conf->ttys; struct lxc_tty_info *ttys = &conf->ttys;
struct lxc_terminal *terminal = &conf->console; struct lxc_terminal *terminal = &conf->console;
for (i = 0; i < ttys->nbtty; i++) { for (i = 0; i < ttys->nbtty; i++)
if (ttys->tty[i].busy == fd) if (ttys->tty[i].busy == fd)
ttys->tty[i].busy = 0; ttys->tty[i].busy = 0;
}
if (terminal->proxy.busy == fd) { if (terminal->proxy.busy != fd)
lxc_mainloop_del_handler(terminal->descr, terminal->proxy.slave); return;
lxc_terminal_peer_proxy_free(terminal);
} lxc_mainloop_del_handler(terminal->descr, terminal->proxy.slave);
lxc_terminal_peer_proxy_free(terminal);
} }
static int lxc_terminal_peer_default(struct lxc_terminal *terminal) static int lxc_terminal_peer_default(struct lxc_terminal *terminal)
...@@ -641,65 +671,69 @@ static int lxc_terminal_peer_default(struct lxc_terminal *terminal) ...@@ -641,65 +671,69 @@ static int lxc_terminal_peer_default(struct lxc_terminal *terminal)
int fd; int fd;
int ret = 0; int ret = 0;
/* If no terminal was given, try current controlling terminal, there if (!path) {
* won't be one if we were started as a daemon (-d). ret = access("/dev/tty", F_OK);
*/ if (ret == 0) {
if (!path && !access("/dev/tty", F_OK)) { /* If no terminal was given, try current controlling
fd = open("/dev/tty", O_RDWR); * terminal, there won't be one if we were started as a
if (fd >= 0) { * daemon (-d).
close(fd); */
path = "/dev/tty"; fd = open("/dev/tty", O_RDWR);
if (fd >= 0) {
close(fd);
path = "/dev/tty";
}
} }
} }
if (!path) { if (!path) {
errno = ENOTTY; errno = ENOTTY;
DEBUG("process does not have a controlling terminal"); DEBUG("Theh process does not have a controlling terminal");
goto out; goto on_succes;
} }
terminal->peer = lxc_unpriv(open(path, O_RDWR | O_CLOEXEC)); terminal->peer = lxc_unpriv(open(path, O_RDWR | O_CLOEXEC));
if (terminal->peer < 0) { if (terminal->peer < 0) {
ERROR("Failed to open \"%s\": %s", path, strerror(errno)); ERROR("%s - Failed to open proxy terminal \"%s\"",
strerror(errno), path);
return -ENOTTY; return -ENOTTY;
} }
DEBUG("using \"%s\" as peer tty device", path); DEBUG("Using terminal \"%s\" as proxy", path);
if (!isatty(terminal->peer)) { if (!isatty(terminal->peer)) {
ERROR("file descriptor for file \"%s\" does not refer to a tty device", path); ERROR("File descriptor for \"%s\" does not refer to a terminal", path);
goto on_error1; goto on_error_free_tios;
} }
ts = lxc_terminal_signal_init(terminal->peer, terminal->master); ts = lxc_terminal_signal_init(terminal->peer, terminal->master);
terminal->tty_state = ts; terminal->tty_state = ts;
if (!ts) { if (!ts) {
WARN("Failed to install signal handler"); WARN("Failed to install signal handler");
goto on_error1; goto on_error_free_tios;
} }
lxc_terminal_winsz(terminal->peer, terminal->master); lxc_terminal_winsz(terminal->peer, terminal->master);
terminal->tios = malloc(sizeof(*terminal->tios)); terminal->tios = malloc(sizeof(*terminal->tios));
if (!terminal->tios) { if (!terminal->tios)
SYSERROR("failed to allocate memory"); goto on_error_free_tios;
goto on_error1;
}
if (lxc_setup_tios(terminal->peer, terminal->tios) < 0) ret = lxc_setup_tios(terminal->peer, terminal->tios);
goto on_error2; if (ret < 0)
goto on_error_close_peer;
else else
goto out; goto on_succes;
on_error2: on_error_free_tios:
free(terminal->tios); free(terminal->tios);
terminal->tios = NULL; terminal->tios = NULL;
on_error1: on_error_close_peer:
close(terminal->peer); close(terminal->peer);
terminal->peer = -1; terminal->peer = -1;
ret = -ENOTTY; ret = -ENOTTY;
out: on_succes:
return ret; return ret;
} }
...@@ -835,13 +869,11 @@ int lxc_terminal_create_log_file(struct lxc_terminal *terminal) ...@@ -835,13 +869,11 @@ int lxc_terminal_create_log_file(struct lxc_terminal *terminal)
int lxc_terminal_create(struct lxc_terminal *terminal) int lxc_terminal_create(struct lxc_terminal *terminal)
{ {
int ret, saved_errno; int ret;
ret = openpty(&terminal->master, &terminal->slave, terminal->name, NULL, ret = openpty(&terminal->master, &terminal->slave, terminal->name, NULL, NULL);
NULL);
saved_errno = errno;
if (ret < 0) { if (ret < 0) {
ERROR("%s - Failed to allocate a pty", strerror(saved_errno)); SYSERROR("Failed to open terminal");
return -1; return -1;
} }
...@@ -859,7 +891,7 @@ int lxc_terminal_create(struct lxc_terminal *terminal) ...@@ -859,7 +891,7 @@ int lxc_terminal_create(struct lxc_terminal *terminal)
ret = lxc_terminal_peer_default(terminal); ret = lxc_terminal_peer_default(terminal);
if (ret < 0) { if (ret < 0) {
ERROR("Failed to allocate a peer pty device"); ERROR("Failed to allocate proxy terminal");
goto err; goto err;
} }
...@@ -875,8 +907,8 @@ int lxc_terminal_setup(struct lxc_conf *conf) ...@@ -875,8 +907,8 @@ int lxc_terminal_setup(struct lxc_conf *conf)
int ret; int ret;
struct lxc_terminal *terminal = &conf->console; struct lxc_terminal *terminal = &conf->console;
if (terminal->path && !strcmp(terminal->path, "none")) { if (terminal->path && strcmp(terminal->path, "none") == 0) {
INFO("No terminal was requested"); INFO("No terminal requested");
return 0; return 0;
} }
...@@ -884,12 +916,10 @@ int lxc_terminal_setup(struct lxc_conf *conf) ...@@ -884,12 +916,10 @@ int lxc_terminal_setup(struct lxc_conf *conf)
if (ret < 0) if (ret < 0)
return -1; return -1;
/* create terminal log file */
ret = lxc_terminal_create_log_file(terminal); ret = lxc_terminal_create_log_file(terminal);
if (ret < 0) if (ret < 0)
goto err; goto err;
/* create terminal ringbuffer */
ret = lxc_terminal_create_ringbuf(terminal); ret = lxc_terminal_create_ringbuf(terminal);
if (ret < 0) if (ret < 0)
goto err; goto err;
...@@ -901,49 +931,56 @@ err: ...@@ -901,49 +931,56 @@ err:
return -ENODEV; return -ENODEV;
} }
static bool __terminal_dup2(int duplicate, int original)
{
int ret;
if (!isatty(original))
return true;
ret = dup2(duplicate, original);
if (ret < 0) {
SYSERROR("Failed to dup2(%d, %d)", duplicate, original);
return false;
}
return true;
}
int lxc_terminal_set_stdfds(int fd) int lxc_terminal_set_stdfds(int fd)
{ {
int i;
if (fd < 0) if (fd < 0)
return 0; return 0;
if (isatty(STDIN_FILENO)) for (i = 0; i < 3; i++)
if (dup2(fd, STDIN_FILENO) < 0) { if (!__terminal_dup2(fd, (int[]){STDIN_FILENO, STDOUT_FILENO,
SYSERROR("failed to duplicate stdin."); STDERR_FILENO}[i]))
return -1;
}
if (isatty(STDOUT_FILENO))
if (dup2(fd, STDOUT_FILENO) < 0) {
SYSERROR("failed to duplicate stdout.");
return -1;
}
if (isatty(STDERR_FILENO))
if (dup2(fd, STDERR_FILENO) < 0) {
SYSERROR("failed to duplicate stderr.");
return -1; return -1;
}
return 0; return 0;
} }
int lxc_terminal_stdin_cb(int fd, uint32_t events, void *cbdata, int lxc_terminal_stdin_cb(int fd, uint32_t events, void *cbdata,
struct lxc_epoll_descr *descr) struct lxc_epoll_descr *descr)
{ {
struct lxc_terminal_state *ts = cbdata; int ret;
char c; char c;
struct lxc_terminal_state *ts = cbdata;
if (fd != ts->stdinfd) if (fd != ts->stdinfd)
return LXC_MAINLOOP_CLOSE; return LXC_MAINLOOP_CLOSE;
if (lxc_read_nointr(ts->stdinfd, &c, 1) <= 0) ret = lxc_read_nointr(ts->stdinfd, &c, 1);
if (ret <= 0)
return LXC_MAINLOOP_CLOSE; return LXC_MAINLOOP_CLOSE;
if (ts->escape >= 1) { if (ts->escape >= 1) {
/* we want to exit the terminal with Ctrl+a q */ /* we want to exit the terminal with Ctrl+a q */
if (c == ts->escape && !ts->saw_escape) { if (c == ts->escape && !ts->saw_escape) {
ts->saw_escape = 1; ts->saw_escape = 1;
return 0; return LXC_MAINLOOP_CONTINUE;
} }
if (c == 'q' && ts->saw_escape) if (c == 'q' && ts->saw_escape)
...@@ -952,18 +989,19 @@ int lxc_terminal_stdin_cb(int fd, uint32_t events, void *cbdata, ...@@ -952,18 +989,19 @@ int lxc_terminal_stdin_cb(int fd, uint32_t events, void *cbdata,
ts->saw_escape = 0; ts->saw_escape = 0;
} }
if (lxc_write_nointr(ts->masterfd, &c, 1) <= 0) ret = lxc_write_nointr(ts->masterfd, &c, 1);
if (ret <= 0)
return LXC_MAINLOOP_CLOSE; return LXC_MAINLOOP_CLOSE;
return 0; return LXC_MAINLOOP_CONTINUE;
} }
int lxc_terminal_master_cb(int fd, uint32_t events, void *cbdata, int lxc_terminal_master_cb(int fd, uint32_t events, void *cbdata,
struct lxc_epoll_descr *descr) struct lxc_epoll_descr *descr)
{ {
struct lxc_terminal_state *ts = cbdata;
char buf[LXC_TERMINAL_BUFFER_SIZE];
int r, w; int r, w;
char buf[LXC_TERMINAL_BUFFER_SIZE];
struct lxc_terminal_state *ts = cbdata;
if (fd != ts->masterfd) if (fd != ts->masterfd)
return LXC_MAINLOOP_CLOSE; return LXC_MAINLOOP_CLOSE;
...@@ -973,14 +1011,10 @@ int lxc_terminal_master_cb(int fd, uint32_t events, void *cbdata, ...@@ -973,14 +1011,10 @@ int lxc_terminal_master_cb(int fd, uint32_t events, void *cbdata,
return LXC_MAINLOOP_CLOSE; return LXC_MAINLOOP_CLOSE;
w = lxc_write_nointr(ts->stdoutfd, buf, r); w = lxc_write_nointr(ts->stdoutfd, buf, r);
if (w <= 0) { if (w <= 0 || w != r)
return LXC_MAINLOOP_CLOSE; return LXC_MAINLOOP_CLOSE;
} else if (w != r) {
SYSERROR("Failed to write");
return 1;
}
return 0; return LXC_MAINLOOP_CONTINUE;
} }
int lxc_terminal_getfd(struct lxc_container *c, int *ttynum, int *masterfd) int lxc_terminal_getfd(struct lxc_container *c, int *ttynum, int *masterfd)
...@@ -992,7 +1026,7 @@ int lxc_console(struct lxc_container *c, int ttynum, ...@@ -992,7 +1026,7 @@ int lxc_console(struct lxc_container *c, int ttynum,
int stdinfd, int stdoutfd, int stderrfd, int stdinfd, int stdoutfd, int stderrfd,
int escape) int escape)
{ {
int ret, ttyfd, masterfd; int masterfd, ret, ttyfd;
struct lxc_epoll_descr descr; struct lxc_epoll_descr descr;
struct termios oldtios; struct termios oldtios;
struct lxc_terminal_state *ts; struct lxc_terminal_state *ts;
...@@ -1021,7 +1055,7 @@ int lxc_console(struct lxc_container *c, int ttynum, ...@@ -1021,7 +1055,7 @@ int lxc_console(struct lxc_container *c, int ttynum,
lxc_terminal_winsz(stdinfd, masterfd); lxc_terminal_winsz(stdinfd, masterfd);
lxc_cmd_terminal_winch(ts->winch_proxy, ts->winch_proxy_lxcpath); lxc_cmd_terminal_winch(ts->winch_proxy, ts->winch_proxy_lxcpath);
} else { } else {
INFO("File descriptor %d does not refer to a tty device", stdinfd); INFO("File descriptor %d does not refer to a terminal", stdinfd);
} }
ret = lxc_mainloop_open(&descr); ret = lxc_mainloop_open(&descr);
...@@ -1097,7 +1131,7 @@ close_fds: ...@@ -1097,7 +1131,7 @@ close_fds:
return ret; return ret;
} }
int lxc_make_controlling_pty(int fd) int lxc_make_controlling_terminal(int fd)
{ {
int ret; int ret;
...@@ -1110,11 +1144,11 @@ int lxc_make_controlling_pty(int fd) ...@@ -1110,11 +1144,11 @@ int lxc_make_controlling_pty(int fd)
return 0; return 0;
} }
int lxc_login_pty(int fd) int lxc_terminal_prepare_login(int fd)
{ {
int ret; int ret;
ret = lxc_make_controlling_pty(fd); ret = lxc_make_controlling_terminal(fd);
if (ret < 0) if (ret < 0)
return -1; return -1;
...@@ -1128,22 +1162,22 @@ int lxc_login_pty(int fd) ...@@ -1128,22 +1162,22 @@ int lxc_login_pty(int fd)
return 0; return 0;
} }
void lxc_terminal_info_init(struct lxc_terminal_info *pty) void lxc_terminal_info_init(struct lxc_terminal_info *terminal)
{ {
pty->name[0] = '\0'; terminal->name[0] = '\0';
pty->master = -EBADF; terminal->master = -EBADF;
pty->slave = -EBADF; terminal->slave = -EBADF;
pty->busy = -1; terminal->busy = -1;
} }
void lxc_terminal_init(struct lxc_terminal *pty) void lxc_terminal_init(struct lxc_terminal *terminal)
{ {
memset(pty, 0, sizeof(*pty)); memset(terminal, 0, sizeof(*terminal));
pty->slave = -EBADF; terminal->slave = -EBADF;
pty->master = -EBADF; terminal->master = -EBADF;
pty->peer = -EBADF; terminal->peer = -EBADF;
pty->log_fd = -EBADF; terminal->log_fd = -EBADF;
lxc_terminal_info_init(&pty->proxy); lxc_terminal_info_init(&terminal->proxy);
} }
void lxc_terminal_conf_free(struct lxc_terminal *terminal) void lxc_terminal_conf_free(struct lxc_terminal *terminal)
...@@ -1154,24 +1188,23 @@ void lxc_terminal_conf_free(struct lxc_terminal *terminal) ...@@ -1154,24 +1188,23 @@ void lxc_terminal_conf_free(struct lxc_terminal *terminal)
lxc_ringbuf_release(&terminal->ringbuf); lxc_ringbuf_release(&terminal->ringbuf);
} }
int lxc_terminal_map_ids(struct lxc_conf *c, struct lxc_terminal *pty) int lxc_terminal_map_ids(struct lxc_conf *c, struct lxc_terminal *terminal)
{ {
int ret; int ret;
if (lxc_list_empty(&c->id_map)) if (lxc_list_empty(&c->id_map))
return 0; return 0;
ret = strcmp(pty->name, ""); if (strcmp(terminal->name, "") == 0)
if (ret == 0)
return 0; return 0;
ret = chown_mapped_root(pty->name, c); ret = chown_mapped_root(terminal->name, c);
if (ret < 0) { if (ret < 0) {
ERROR("Failed to chown \"%s\"", pty->name); ERROR("Failed to chown terminal \"%s\"", terminal->name);
return -1; return -1;
} }
TRACE("Chowned \"%s\"", pty->name); TRACE("Chowned terminal \"%s\"", terminal->name);
return 0; return 0;
} }
...@@ -21,8 +21,8 @@ ...@@ -21,8 +21,8 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/ */
#ifndef __LXC_CONSOLE_H #ifndef __LXC_TERMINAL_H
#define __LXC_CONSOLE_H #define __LXC_TERMINAL_H
#include "config.h" #include "config.h"
...@@ -36,15 +36,17 @@ struct lxc_container; ...@@ -36,15 +36,17 @@ struct lxc_container;
struct lxc_conf; struct lxc_conf;
struct lxc_epoll_descr; struct lxc_epoll_descr;
/* Defines a structure containing a pty information for virtualizing a tty
* @name : the path name of the slave pty side
* @master : the file descriptor of the master
* @slave : the file descriptor of the slave
*/
struct lxc_terminal_info { struct lxc_terminal_info {
/* the path name of the slave side */
char name[MAXPATHLEN]; char name[MAXPATHLEN];
/* the file descriptor of the master */
int master; int master;
/* the file descriptor of the slave */
int slave; int slave;
/* whether the terminal is currently used */
int busy; int busy;
}; };
...@@ -53,25 +55,32 @@ struct lxc_terminal_state { ...@@ -53,25 +55,32 @@ struct lxc_terminal_state {
int stdinfd; int stdinfd;
int stdoutfd; int stdoutfd;
int masterfd; int masterfd;
/* Escape sequence to use for exiting the pty. A single char can be
* specified. The pty can then exited by doing: Ctrl + specified_char + /* Escape sequence to use for exiting the terminal. A single char can
* q. This field is checked by lxc_terminal_stdin_cb(). Set to -1 to * be specified. The terminal can then exited by doing: Ctrl +
* disable exiting the pty via a escape sequence. * specified_char + q. This field is checked by
* lxc_terminal_stdin_cb(). Set to -1 to disable exiting the terminal
* via a escape sequence.
*/ */
int escape; int escape;
/* Used internally by lxc_terminal_stdin_cb() to check whether an /* Used internally by lxc_terminal_stdin_cb() to check whether an
* escape sequence has been received. * escape sequence has been received.
*/ */
int saw_escape; int saw_escape;
/* Name of the container to forward the SIGWINCH event to. */ /* Name of the container to forward the SIGWINCH event to. */
const char *winch_proxy; const char *winch_proxy;
/* Path of the container to forward the SIGWINCH event to. */ /* Path of the container to forward the SIGWINCH event to. */
const char *winch_proxy_lxcpath; const char *winch_proxy_lxcpath;
/* File descriptor that accepts signals. If set to -1 no signal handler /* File descriptor that accepts signals. If set to -1 no signal handler
* could be installed. This also means that the sigset_t oldmask member * could be installed. This also means that the sigset_t oldmask member
* is meaningless. * is meaningless.
*/ */
int sigfd; int sigfd;
sigset_t oldmask; sigset_t oldmask;
}; };
...@@ -86,7 +95,7 @@ struct lxc_terminal { ...@@ -86,7 +95,7 @@ struct lxc_terminal {
struct termios *tios; struct termios *tios;
struct lxc_terminal_state *tty_state; struct lxc_terminal_state *tty_state;
struct /* lxc_console_log */ { struct /* lxc_terminal_log */ {
/* size of the log file */ /* size of the log file */
uint64_t log_size; uint64_t log_size;
...@@ -109,7 +118,7 @@ struct lxc_terminal { ...@@ -109,7 +118,7 @@ struct lxc_terminal {
}; };
}; };
/* /**
* lxc_terminal_allocate: allocate the console or a tty * lxc_terminal_allocate: allocate the console or a tty
* *
* @conf : the configuration of the container to allocate from * @conf : the configuration of the container to allocate from
...@@ -119,61 +128,54 @@ struct lxc_terminal { ...@@ -119,61 +128,54 @@ struct lxc_terminal {
*/ */
extern int lxc_terminal_allocate(struct lxc_conf *conf, int sockfd, int *ttynum); extern int lxc_terminal_allocate(struct lxc_conf *conf, int sockfd, int *ttynum);
/* /**
* Create a new pty: * Create a new terminal:
* - calls openpty() to allocate a master/slave pty pair * - calls openpty() to allocate a master/slave pair
* - sets the FD_CLOEXEC flag on the master/slave fds * - sets the FD_CLOEXEC flag on the master/slave fds
* - allocates either the current controlling pty (default) or a user specified * - allocates either the current controlling terminal (default) or a user
* pty as peer pty for the newly created master/slave pair * specified terminal as proxy for the newly created master/slave pair
* - sets up SIGWINCH handler, winsz, and new terminal settings * - sets up SIGWINCH handler, winsz, and new terminal settings
* (Handlers for SIGWINCH and I/O are not registered in a mainloop.) * (Handlers for SIGWINCH and I/O are not registered in a mainloop.)
* (For an unprivileged container the created pty on the host is not
* automatically chowned to the uid/gid of the unprivileged user. For this
* ttys_shift_ids() can be called.)
*/ */
extern int lxc_terminal_create(struct lxc_terminal *console); extern int lxc_terminal_create(struct lxc_terminal *console);
/** /**
* lxc_terminal_setup: Create a new pty. * lxc_terminal_setup: Create a new terminal.
* - In addition to lxc_terminal_create() also sets up all pty logs. * - In addition to lxc_terminal_create() also sets up logging.
*/ */
extern int lxc_terminal_setup(struct lxc_conf *); extern int lxc_terminal_setup(struct lxc_conf *);
/* /**
* Delete a pty created via lxc_terminal_setup(): * Delete a terminal created via lxc_terminal_create() or lxc_terminal_setup():
* - set old terminal settings * Note, registered handlers are not automatically deleted.
* - memory allocated via lxc_terminal_setup() is free()ed.
* - close master/slave pty pair and allocated fd for the peer (usually
* /dev/tty)
* Registered handlers in a mainloop are not automatically deleted.
*/ */
extern void lxc_terminal_delete(struct lxc_terminal *); extern void lxc_terminal_delete(struct lxc_terminal *);
/* /**
* lxc_terminal_free: mark the console or a tty as unallocated, free any * lxc_terminal_free: mark the terminal as unallocated and free any resources
* resources allocated by lxc_terminal_allocate(). * allocated by lxc_terminal_allocate().
* *
* @conf : the configuration of the container whose tty was closed * @conf : the configuration of the container whose tty was closed
* @fd : the socket fd whose remote side was closed, which indicated * @fd : the socket fd whose remote side was closed, which indicated
* the console or tty is no longer in use. this is used to match * the terminal is no longer in use. this is used to match
* which console/tty is being freed. * which terminal is being freed.
*/ */
extern void lxc_terminal_free(struct lxc_conf *conf, int fd); extern void lxc_terminal_free(struct lxc_conf *conf, int fd);
/* /**
* Register pty event handlers in an open mainloop * Register terminal event handlers in an open mainloop.
*/ */
extern int lxc_terminal_mainloop_add(struct lxc_epoll_descr *, struct lxc_terminal *); extern int lxc_terminal_mainloop_add(struct lxc_epoll_descr *, struct lxc_terminal *);
/* /**
* Handle SIGWINCH events on the allocated ptys. * Handle SIGWINCH events on the allocated terminals.
*/ */
extern void lxc_terminal_sigwinch(int sig); extern void lxc_terminal_sigwinch(int sig);
/* /**
* Connect to one of the ptys given to the container via lxc.tty.max. * Connect to one of the ttys given to the container via lxc.tty.max.
* - allocates either the current controlling pty (default) or a user specified * - allocates either the current controlling terminal (default) or a user specified
* pty as peer pty for the containers tty * terminal as proxy terminal for the containers tty
* - sets up SIGWINCH handler, winsz, and new terminal settings * - sets up SIGWINCH handler, winsz, and new terminal settings
* - opens mainloop * - opens mainloop
* - registers SIGWINCH, I/O handlers in the mainloop * - registers SIGWINCH, I/O handlers in the mainloop
...@@ -183,24 +185,24 @@ extern int lxc_console(struct lxc_container *c, int ttynum, ...@@ -183,24 +185,24 @@ extern int lxc_console(struct lxc_container *c, int ttynum,
int stdinfd, int stdoutfd, int stderrfd, int stdinfd, int stdoutfd, int stderrfd,
int escape); int escape);
/* /**
* Allocate one of the ptys given to the container via lxc.tty.max. Returns an * Allocate one of the tty given to the container via lxc.tty.max. Returns an
* open fd to the allocated pty. * open fd to the allocated tty.
* Set ttynum to -1 to allocate the first available pty, or to a value within * Set ttynum to -1 to allocate the first available tty, or to a value within
* the range specified by lxc.tty.max to allocate a specific pty. * the range specified by lxc.tty.max to allocate a specific tty.
*/ */
extern int lxc_terminal_getfd(struct lxc_container *c, int *ttynum, extern int lxc_terminal_getfd(struct lxc_container *c, int *ttynum,
int *masterfd); int *masterfd);
/* /**
* Make fd a duplicate of the standard file descriptors: * Make fd a duplicate of the standard file descriptors. The fd is made a
* fd is made a duplicate of a specific standard file descriptor iff the * duplicate of a specific standard file descriptor iff the standard file
* standard file descriptor refers to a pty. * descriptor refers to a terminal.
*/ */
extern int lxc_terminal_set_stdfds(int fd); extern int lxc_terminal_set_stdfds(int fd);
/* /**
* Handler for events on the stdin fd of the pty. To be registered via the * Handler for events on the stdin fd of the terminal. To be registered via the
* corresponding functions declared and defined in mainloop.{c,h} or * corresponding functions declared and defined in mainloop.{c,h} or
* lxc_terminal_mainloop_add(). * lxc_terminal_mainloop_add().
* This function exits the loop cleanly when an EPOLLHUP event is received. * This function exits the loop cleanly when an EPOLLHUP event is received.
...@@ -208,42 +210,46 @@ extern int lxc_terminal_set_stdfds(int fd); ...@@ -208,42 +210,46 @@ extern int lxc_terminal_set_stdfds(int fd);
extern int lxc_terminal_stdin_cb(int fd, uint32_t events, void *cbdata, extern int lxc_terminal_stdin_cb(int fd, uint32_t events, void *cbdata,
struct lxc_epoll_descr *descr); struct lxc_epoll_descr *descr);
/* /**
* Handler for events on the master fd of the pty. To be registered via the * Handler for events on the master fd of the terminal. To be registered via
* corresponding functions declared and defined in mainloop.{c,h} or * the corresponding functions declared and defined in mainloop.{c,h} or
* lxc_terminal_mainloop_add(). * lxc_terminal_mainloop_add().
* This function exits the loop cleanly when an EPOLLHUP event is received. * This function exits the loop cleanly when an EPOLLHUP event is received.
*/ */
extern int lxc_terminal_master_cb(int fd, uint32_t events, void *cbdata, extern int lxc_terminal_master_cb(int fd, uint32_t events, void *cbdata,
struct lxc_epoll_descr *descr); struct lxc_epoll_descr *descr);
/* /**
* Setup new terminal properties. The old terminal settings are stored in * Setup new terminal properties. The old terminal settings are stored in
* oldtios. * oldtios.
*/ */
extern int lxc_setup_tios(int fd, struct termios *oldtios); extern int lxc_setup_tios(int fd, struct termios *oldtios);
/* /**
* lxc_terminal_winsz: propagate winsz from one terminal to another * lxc_terminal_winsz: propagate winsz from one terminal to another
* *
* @srcfd : terminal to get size from (typically a slave pty) * @srcfd
* @dstfd : terminal to set size on (typically a master pty) * - terminal to get size from (typically a slave pty)
* @dstfd
* - terminal to set size on (typically a master pty)
*/ */
extern void lxc_terminal_winsz(int srcfd, int dstfd); extern void lxc_terminal_winsz(int srcfd, int dstfd);
/* /*
* lxc_terminal_signal_init: install signal handler * lxc_terminal_signal_init: install signal handler
* *
* @srcfd : src for winsz in SIGWINCH handler * @srcfd
* @dstfd : dst for winsz in SIGWINCH handler * - src for winsz in SIGWINCH handler
* @dstfd
* - dst for winsz in SIGWINCH handler
* *
* Returns lxc_terminal_state structure on success or NULL on failure. The sigfd * Returns lxc_terminal_state structure on success or NULL on failure. The
* member of the returned lxc_terminal_state can be select()/poll()ed/epoll()ed * sigfd member of the returned lxc_terminal_state can be
* on (ie added to a mainloop) for signals. * select()/poll()ed/epoll()ed on (i.e. added to a mainloop) for signals.
* *
* Must be called with process_lock held to protect the lxc_ttys list, or * Must be called with process_lock held to protect the lxc_ttys list, or from
* from a non-threaded context. * a non-threaded context.
* *
* Note that the signal handler isn't installed as a classic asychronous * Note that the signal handler isn't installed as a classic asychronous
* handler, rather signalfd(2) is used so that we can handle the signal when * handler, rather signalfd(2) is used so that we can handle the signal when
...@@ -256,17 +262,18 @@ extern void lxc_terminal_winsz(int srcfd, int dstfd); ...@@ -256,17 +262,18 @@ extern void lxc_terminal_winsz(int srcfd, int dstfd);
*/ */
extern struct lxc_terminal_state *lxc_terminal_signal_init(int srcfd, int dstfd); extern struct lxc_terminal_state *lxc_terminal_signal_init(int srcfd, int dstfd);
/* /**
* Handler for signal events. To be registered via the corresponding functions * Handler for signal events. To be registered via the corresponding functions
* declared and defined in mainloop.{c,h} or lxc_terminal_mainloop_add(). * declared and defined in mainloop.{c,h} or lxc_terminal_mainloop_add().
*/ */
extern int lxc_terminal_signalfd_cb(int fd, uint32_t events, void *cbdata, extern int lxc_terminal_signalfd_cb(int fd, uint32_t events, void *cbdata,
struct lxc_epoll_descr *descr); struct lxc_epoll_descr *descr);
/* /**
* lxc_terminal_signal_fini: uninstall signal handler * lxc_terminal_signal_fini: uninstall signal handler
* *
* @ts : the lxc_terminal_state returned by lxc_terminal_signal_init * @ts
* - the lxc_terminal_state returned by lxc_terminal_signal_init
* *
* Restore the saved signal handler that was in effect at the time * Restore the saved signal handler that was in effect at the time
* lxc_terminal_signal_init() was called. * lxc_terminal_signal_init() was called.
...@@ -276,16 +283,17 @@ extern int lxc_terminal_signalfd_cb(int fd, uint32_t events, void *cbdata, ...@@ -276,16 +283,17 @@ extern int lxc_terminal_signalfd_cb(int fd, uint32_t events, void *cbdata,
*/ */
extern void lxc_terminal_signal_fini(struct lxc_terminal_state *ts); extern void lxc_terminal_signal_fini(struct lxc_terminal_state *ts);
extern int lxc_terminal_write_ringbuffer(struct lxc_terminal *console); extern int lxc_terminal_write_ringbuffer(struct lxc_terminal *terminal);
extern int lxc_terminal_create_log_file(struct lxc_terminal *console); extern int lxc_terminal_create_log_file(struct lxc_terminal *terminal);
extern int lxc_terminal_io_cb(int fd, uint32_t events, void *data, extern int lxc_terminal_io_cb(int fd, uint32_t events, void *data,
struct lxc_epoll_descr *descr); struct lxc_epoll_descr *descr);
extern int lxc_make_controlling_pty(int fd); extern int lxc_make_controlling_terminal(int fd);
extern int lxc_login_pty(int fd); extern int lxc_terminal_prepare_login(int fd);
extern void lxc_terminal_conf_free(struct lxc_terminal *console); extern void lxc_terminal_conf_free(struct lxc_terminal *terminal);
extern void lxc_terminal_info_init(struct lxc_terminal_info *pty); extern void lxc_terminal_info_init(struct lxc_terminal_info *terminal);
extern void lxc_terminal_init(struct lxc_terminal *pty); extern void lxc_terminal_init(struct lxc_terminal *terminal);
extern int lxc_terminal_map_ids(struct lxc_conf *c, struct lxc_terminal *pty); extern int lxc_terminal_map_ids(struct lxc_conf *c,
struct lxc_terminal *terminal);
#endif #endif /* __LXC_TERMINAL_H */
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment