mirror of
https://git.proxmox.com/git/mirror_lxc
synced 2025-08-09 05:31:07 +00:00
separate console device from console log
lxc-start -c makes the named file/device the container's console, but using this with a regular file in order to get a log of the console output does not work very well if you also want to login on the console. This change implements an additional option (-L) to simply log the console's output to a file. Both options can be used separately or together. For example to get a usable console and log: lxc-start -n name -c /dev/tty8 -L console.log The console state is cleaned up more when lxc_delete_console is called, and some of the clean up paths in lxc_create_console were fixed. The lxc_priv and lxc_unpriv macros were modified to make use of gcc's local label feature so they can be expanded more than once in the same function. Signed-off-by: Dwight Engen <dwight.engen@oracle.com> Acked-by: Serge E. Hallyn <serge.hallyn@ubuntu.com>
This commit is contained in:
parent
4199da3061
commit
596a818d4b
@ -51,7 +51,8 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|||||||
<command>lxc-start</command>
|
<command>lxc-start</command>
|
||||||
<arg choice="req">-n <replaceable>name</replaceable></arg>
|
<arg choice="req">-n <replaceable>name</replaceable></arg>
|
||||||
<arg choice="opt">-f <replaceable>config_file</replaceable></arg>
|
<arg choice="opt">-f <replaceable>config_file</replaceable></arg>
|
||||||
<arg choice="opt">-c <replaceable>console_file</replaceable></arg>
|
<arg choice="opt">-c <replaceable>console_device</replaceable></arg>
|
||||||
|
<arg choice="opt">-L <replaceable>console_logfile</replaceable></arg>
|
||||||
<arg choice="opt">-d</arg>
|
<arg choice="opt">-d</arg>
|
||||||
<arg choice="opt">-p <replaceable>pid_file</replaceable></arg>
|
<arg choice="opt">-p <replaceable>pid_file</replaceable></arg>
|
||||||
<arg choice="opt">-s KEY=VAL</arg>
|
<arg choice="opt">-s KEY=VAL</arg>
|
||||||
@ -75,11 +76,6 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|||||||
If no configuration is
|
If no configuration is
|
||||||
defined, the default isolation is used.
|
defined, the default isolation is used.
|
||||||
</para>
|
</para>
|
||||||
<para>
|
|
||||||
The orphan process group
|
|
||||||
and daemon are not supported by this command, use
|
|
||||||
the <command>lxc-execute</command> command instead.
|
|
||||||
</para>
|
|
||||||
<para>
|
<para>
|
||||||
If no command is specified, <command>lxc-start</command> will
|
If no command is specified, <command>lxc-start</command> will
|
||||||
use the default
|
use the default
|
||||||
@ -139,13 +135,25 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|||||||
<varlistentry>
|
<varlistentry>
|
||||||
<term>
|
<term>
|
||||||
<option>-c,
|
<option>-c,
|
||||||
--console <replaceable>console_file</replaceable></option>
|
--console <replaceable>console_device</replaceable></option>
|
||||||
</term>
|
</term>
|
||||||
<listitem>
|
<listitem>
|
||||||
<para>
|
<para>
|
||||||
Specify a file to output the container console. If the
|
Specify a device to use for the container's console, for example
|
||||||
option is not specified the output will go the terminal
|
/dev/tty8. If this option is not specified the current terminal
|
||||||
except if the <option>-d</option> is specified.
|
will be used unless <option>-d</option> is specified.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</varlistentry>
|
||||||
|
|
||||||
|
<varlistentry>
|
||||||
|
<term>
|
||||||
|
<option>-L,
|
||||||
|
--console-log <replaceable>console_logfile</replaceable></option>
|
||||||
|
</term>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
Specify a file to log the container's console output to.
|
||||||
</para>
|
</para>
|
||||||
</listitem>
|
</listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
|
@ -45,6 +45,7 @@ struct lxc_arguments {
|
|||||||
int daemonize;
|
int daemonize;
|
||||||
const char *rcfile;
|
const char *rcfile;
|
||||||
const char *console;
|
const char *console;
|
||||||
|
const char *console_log;
|
||||||
const char *pidfile;
|
const char *pidfile;
|
||||||
|
|
||||||
/* for lxc-checkpoint/restart */
|
/* for lxc-checkpoint/restart */
|
||||||
|
@ -33,27 +33,29 @@ extern int lxc_caps_last_cap(void);
|
|||||||
|
|
||||||
#define lxc_priv(__lxc_function) \
|
#define lxc_priv(__lxc_function) \
|
||||||
({ \
|
({ \
|
||||||
|
__label__ out; \
|
||||||
int __ret, __ret2, __errno = 0; \
|
int __ret, __ret2, __errno = 0; \
|
||||||
__ret = lxc_caps_up(); \
|
__ret = lxc_caps_up(); \
|
||||||
if (__ret) \
|
if (__ret) \
|
||||||
goto __out; \
|
goto out; \
|
||||||
__ret = __lxc_function; \
|
__ret = __lxc_function; \
|
||||||
if (__ret) \
|
if (__ret) \
|
||||||
__errno = errno; \
|
__errno = errno; \
|
||||||
__ret2 = lxc_caps_down(); \
|
__ret2 = lxc_caps_down(); \
|
||||||
__out: __ret ? errno = __errno,__ret : __ret2; \
|
out: __ret ? errno = __errno,__ret : __ret2; \
|
||||||
})
|
})
|
||||||
|
|
||||||
#define lxc_unpriv(__lxc_function) \
|
#define lxc_unpriv(__lxc_function) \
|
||||||
({ \
|
({ \
|
||||||
|
__label__ out; \
|
||||||
int __ret, __ret2, __errno = 0; \
|
int __ret, __ret2, __errno = 0; \
|
||||||
__ret = lxc_caps_down(); \
|
__ret = lxc_caps_down(); \
|
||||||
if (__ret) \
|
if (__ret) \
|
||||||
goto __out; \
|
goto out; \
|
||||||
__ret = __lxc_function; \
|
__ret = __lxc_function; \
|
||||||
if (__ret) \
|
if (__ret) \
|
||||||
__errno = errno; \
|
__errno = errno; \
|
||||||
__ret2 = lxc_caps_up(); \
|
__ret2 = lxc_caps_up(); \
|
||||||
__out: __ret ? errno = __errno,__ret : __ret2; \
|
out: __ret ? errno = __errno,__ret : __ret2; \
|
||||||
})
|
})
|
||||||
#endif
|
#endif
|
||||||
|
@ -2005,6 +2005,8 @@ struct lxc_conf *lxc_conf_init(void)
|
|||||||
memset(new, 0, sizeof(*new));
|
memset(new, 0, sizeof(*new));
|
||||||
|
|
||||||
new->personality = -1;
|
new->personality = -1;
|
||||||
|
new->console.log_path = NULL;
|
||||||
|
new->console.log_fd = -1;
|
||||||
new->console.path = NULL;
|
new->console.path = NULL;
|
||||||
new->console.peer = -1;
|
new->console.peer = -1;
|
||||||
new->console.master = -1;
|
new->console.master = -1;
|
||||||
|
@ -175,6 +175,8 @@ struct lxc_console {
|
|||||||
int master;
|
int master;
|
||||||
int peer;
|
int peer;
|
||||||
char *path;
|
char *path;
|
||||||
|
char *log_path;
|
||||||
|
int log_fd;
|
||||||
char name[MAXPATHLEN];
|
char name[MAXPATHLEN];
|
||||||
struct termios *tios;
|
struct termios *tios;
|
||||||
};
|
};
|
||||||
|
@ -195,11 +195,21 @@ int lxc_create_console(struct lxc_conf *conf)
|
|||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (console->log_path) {
|
||||||
|
fd = lxc_unpriv(open(console->log_path, O_CLOEXEC | O_RDWR | O_CREAT | O_APPEND, 0600));
|
||||||
|
if (fd < 0) {
|
||||||
|
SYSERROR("failed to open '%s'", console->log_path);
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
DEBUG("using '%s' as console log", console->log_path);
|
||||||
|
console->log_fd = fd;
|
||||||
|
}
|
||||||
|
|
||||||
fd = lxc_unpriv(open(console->path, O_CLOEXEC | O_RDWR | O_CREAT |
|
fd = lxc_unpriv(open(console->path, O_CLOEXEC | O_RDWR | O_CREAT |
|
||||||
O_APPEND, 0600));
|
O_APPEND, 0600));
|
||||||
if (fd < 0) {
|
if (fd < 0) {
|
||||||
SYSERROR("failed to open '%s'", console->path);
|
SYSERROR("failed to open '%s'", console->path);
|
||||||
goto err;
|
goto err_close_console_log;
|
||||||
}
|
}
|
||||||
|
|
||||||
DEBUG("using '%s' as console", console->path);
|
DEBUG("using '%s' as console", console->path);
|
||||||
@ -212,7 +222,7 @@ int lxc_create_console(struct lxc_conf *conf)
|
|||||||
console->tios = malloc(sizeof(tios));
|
console->tios = malloc(sizeof(tios));
|
||||||
if (!console->tios) {
|
if (!console->tios) {
|
||||||
SYSERROR("failed to allocate memory");
|
SYSERROR("failed to allocate memory");
|
||||||
goto err;
|
goto err_close_console;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Get termios */
|
/* Get termios */
|
||||||
@ -241,26 +251,54 @@ int lxc_create_console(struct lxc_conf *conf)
|
|||||||
|
|
||||||
err_free:
|
err_free:
|
||||||
free(console->tios);
|
free(console->tios);
|
||||||
|
|
||||||
|
err_close_console:
|
||||||
|
close(console->peer);
|
||||||
|
console->peer = -1;
|
||||||
|
|
||||||
|
err_close_console_log:
|
||||||
|
if (console->log_fd >= 0) {
|
||||||
|
close(console->log_fd);
|
||||||
|
console->log_fd = -1;
|
||||||
|
}
|
||||||
|
|
||||||
err:
|
err:
|
||||||
close(console->master);
|
close(console->master);
|
||||||
|
console->master = -1;
|
||||||
|
|
||||||
close(console->slave);
|
close(console->slave);
|
||||||
|
console->slave = -1;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
void lxc_delete_console(const struct lxc_console *console)
|
void lxc_delete_console(struct lxc_console *console)
|
||||||
{
|
{
|
||||||
if (console->tios &&
|
if (console->tios &&
|
||||||
tcsetattr(console->peer, TCSAFLUSH, console->tios))
|
tcsetattr(console->peer, TCSAFLUSH, console->tios))
|
||||||
WARN("failed to set old terminal settings");
|
WARN("failed to set old terminal settings");
|
||||||
|
free(console->tios);
|
||||||
|
console->tios = NULL;
|
||||||
|
|
||||||
|
close(console->peer);
|
||||||
|
console->peer = -1;
|
||||||
|
|
||||||
|
if (console->log_fd >= 0) {
|
||||||
|
close(console->log_fd);
|
||||||
|
console->log_fd = -1;
|
||||||
|
}
|
||||||
|
|
||||||
close(console->master);
|
close(console->master);
|
||||||
|
console->master = -1;
|
||||||
|
|
||||||
close(console->slave);
|
close(console->slave);
|
||||||
|
console->slave = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int console_handler(int fd, void *data, struct lxc_epoll_descr *descr)
|
static int console_handler(int fd, void *data, struct lxc_epoll_descr *descr)
|
||||||
{
|
{
|
||||||
struct lxc_console *console = (struct lxc_console *)data;
|
struct lxc_console *console = (struct lxc_console *)data;
|
||||||
char buf[1024];
|
char buf[1024];
|
||||||
int r;
|
int r,w;
|
||||||
|
|
||||||
r = read(fd, buf, sizeof(buf));
|
r = read(fd, buf, sizeof(buf));
|
||||||
if (r < 0) {
|
if (r < 0) {
|
||||||
@ -280,10 +318,14 @@ static int console_handler(int fd, void *data, struct lxc_epoll_descr *descr)
|
|||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if (console->peer == fd)
|
if (console->peer == fd)
|
||||||
r = write(console->master, buf, r);
|
w = write(console->master, buf, r);
|
||||||
else
|
else {
|
||||||
r = write(console->peer, buf, r);
|
w = write(console->peer, buf, r);
|
||||||
|
if (console->log_fd > 0)
|
||||||
|
w = write(console->log_fd, buf, r);
|
||||||
|
}
|
||||||
|
if (w != r)
|
||||||
|
WARN("console short write");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -54,10 +54,46 @@ lxc_log_define(lxc_start_ui, lxc_start);
|
|||||||
|
|
||||||
static struct lxc_list defines;
|
static struct lxc_list defines;
|
||||||
|
|
||||||
|
static int ensure_path(char **confpath, const char *path)
|
||||||
|
{
|
||||||
|
int err = -1, fd;
|
||||||
|
char *fullpath = NULL;
|
||||||
|
|
||||||
|
if (path) {
|
||||||
|
if (access(path, W_OK)) {
|
||||||
|
fd = creat(path, 0600);
|
||||||
|
if (fd < 0) {
|
||||||
|
SYSERROR("failed to create '%s'", path);
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
close(fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
fullpath = realpath(path, NULL);
|
||||||
|
if (!fullpath) {
|
||||||
|
SYSERROR("failed to get the real path of '%s'", path);
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
*confpath = strdup(fullpath);
|
||||||
|
if (!*confpath) {
|
||||||
|
ERROR("failed to dup string '%s'", fullpath);
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
err = 0;
|
||||||
|
|
||||||
|
err:
|
||||||
|
if (fullpath)
|
||||||
|
free(fullpath);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
static int my_parser(struct lxc_arguments* args, int c, char* arg)
|
static int my_parser(struct lxc_arguments* args, int c, char* arg)
|
||||||
{
|
{
|
||||||
switch (c) {
|
switch (c) {
|
||||||
case 'c': args->console = arg; break;
|
case 'c': args->console = arg; break;
|
||||||
|
case 'L': args->console_log = arg; break;
|
||||||
case 'd': args->daemonize = 1; args->close_all_fds = 1; break;
|
case 'd': args->daemonize = 1; args->close_all_fds = 1; break;
|
||||||
case 'f': args->rcfile = arg; break;
|
case 'f': args->rcfile = arg; break;
|
||||||
case 'C': args->close_all_fds = 1; break;
|
case 'C': args->close_all_fds = 1; break;
|
||||||
@ -72,6 +108,7 @@ static const struct option my_longopts[] = {
|
|||||||
{"rcfile", required_argument, 0, 'f'},
|
{"rcfile", required_argument, 0, 'f'},
|
||||||
{"define", required_argument, 0, 's'},
|
{"define", required_argument, 0, 's'},
|
||||||
{"console", required_argument, 0, 'c'},
|
{"console", required_argument, 0, 'c'},
|
||||||
|
{"console-log", required_argument, 0, 'L'},
|
||||||
{"close-all-fds", no_argument, 0, 'C'},
|
{"close-all-fds", no_argument, 0, 'C'},
|
||||||
{"pidfile", required_argument, 0, 'p'},
|
{"pidfile", required_argument, 0, 'p'},
|
||||||
LXC_COMMON_OPTIONS
|
LXC_COMMON_OPTIONS
|
||||||
@ -89,7 +126,8 @@ Options :\n\
|
|||||||
-d, --daemon daemonize the container\n\
|
-d, --daemon daemonize the container\n\
|
||||||
-p, --pidfile=FILE Create a file with the process id\n\
|
-p, --pidfile=FILE Create a file with the process id\n\
|
||||||
-f, --rcfile=FILE Load configuration file FILE\n\
|
-f, --rcfile=FILE Load configuration file FILE\n\
|
||||||
-c, --console=FILE Set the file output for the container console\n\
|
-c, --console=FILE Use specified FILE for the container console\n\
|
||||||
|
-L, --console-log=FILE Log container console output to FILE\n\
|
||||||
-C, --close-all-fds If any fds are inherited, close them\n\
|
-C, --close-all-fds If any fds are inherited, close them\n\
|
||||||
If not specified, exit with failure instead\n\
|
If not specified, exit with failure instead\n\
|
||||||
Note: --daemon implies --close-all-fds\n\
|
Note: --daemon implies --close-all-fds\n\
|
||||||
@ -177,37 +215,16 @@ int main(int argc, char *argv[])
|
|||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (my_args.console) {
|
if (ensure_path(&conf->console.path, my_args.console) < 0) {
|
||||||
|
ERROR("failed to ensure console path '%s'", my_args.console);
|
||||||
char *console, fd;
|
|
||||||
|
|
||||||
if (access(my_args.console, W_OK)) {
|
|
||||||
|
|
||||||
fd = creat(my_args.console, 0600);
|
|
||||||
if (fd < 0) {
|
|
||||||
SYSERROR("failed to touch file '%s'",
|
|
||||||
my_args.console);
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
close(fd);
|
|
||||||
}
|
|
||||||
|
|
||||||
console = realpath(my_args.console, NULL);
|
|
||||||
if (!console) {
|
|
||||||
SYSERROR("failed to get the real path of '%s'",
|
|
||||||
my_args.console);
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
conf->console.path = strdup(console);
|
if (ensure_path(&conf->console.log_path, my_args.console_log) < 0) {
|
||||||
if (!conf->console.path) {
|
ERROR("failed to ensure console log '%s'", my_args.console_log);
|
||||||
ERROR("failed to dup string '%s'", console);
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
free(console);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (my_args.pidfile != NULL) {
|
if (my_args.pidfile != NULL) {
|
||||||
pid_fp = fopen(my_args.pidfile, "w");
|
pid_fp = fopen(my_args.pidfile, "w");
|
||||||
if (pid_fp == NULL) {
|
if (pid_fp == NULL) {
|
||||||
|
Loading…
Reference in New Issue
Block a user