Merge pull request #2026 from brauner/2017-12-12/lxc_hook_version

confile: add lxc.hook.version
This commit is contained in:
Serge Hallyn 2017-12-14 09:27:46 -06:00 committed by GitHub
commit 81b10e37c9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 535 additions and 166 deletions

View File

@ -308,9 +308,9 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
<title>Init ID</title> <title>Init ID</title>
<para> <para>
Sets the UID/GID to use for the init system, and subsequent commands. Sets the UID/GID to use for the init system, and subsequent commands.
Note that using a non-root uid when booting a system container will Note that using a non-root UID when booting a system container will
likely not work due to missing privileges. Setting the UID/GID is mostly likely not work due to missing privileges. Setting the UID/GID is mostly
useful when running application container. useful when running application containers.
Defaults to: UID(0), GID(0) Defaults to: UID(0), GID(0)
</para> </para>
@ -608,7 +608,6 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
</listitem> </listitem>
</varlistentry> </varlistentry>
<varlistentry> <varlistentry>
<term> <term>
<option>lxc.net.[i].ipv6.address</option> <option>lxc.net.[i].ipv6.address</option>
@ -651,15 +650,56 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
<para> <para>
Add a configuration option to specify a script to be Add a configuration option to specify a script to be
executed after creating and configuring the network used executed after creating and configuring the network used
from the host side. The following arguments are passed from the host side.
to the script: container name and config section name
(net) Additional arguments depend on the config section
employing a script hook; the following are used by the
network system: execution context (up), network type
(empty/veth/macvlan/phys), Depending on the network
type, other arguments may be passed:
veth/macvlan/phys. And finally (host-sided) device name.
</para> </para>
<para>
In addition to the information available to all hooks. The
following information is provided to the script:
<itemizedlist>
<listitem>
<para>
LXC_HOOK_TYPE: the hook type. This is either 'up' or 'down'.
</para>
</listitem>
<listitem>
<para>
LXC_HOOK_SECTION: the section type 'net'.
</para>
</listitem>
<listitem>
<para>
LXC_NET_TYPE: the network type. This is one of the valid
network types listed here (e.g. 'macvlan', 'veth').
</para>
</listitem>
<listitem>
<para>
LXC_NET_PARENT: the parent device on the host. This is only
set for network types 'mavclan', 'veth', 'phys'.
</para>
</listitem>
<listitem>
<para>
LXC_NET_PEER: the name of the peer device on the host. This is
only set for 'veth' network types. Note that this information
is only available when <option>lxc.hook.version</option> is set
to 1.
</para>
</listitem>
</itemizedlist>
Whether this information is provided in the form of environment
variables or as arguments to the script depends on the value of
<option>lxc.hook.version</option>. If set to 1 then information is
provided in the form of environment variables. If set to 0
information is provided as arguments to the script.
</para>
<para> <para>
Standard output from the script is logged at debug level. Standard output from the script is logged at debug level.
Standard error is not logged, but can be captured by the Standard error is not logged, but can be captured by the
@ -676,15 +716,56 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
<para> <para>
Add a configuration option to specify a script to be Add a configuration option to specify a script to be
executed before destroying the network used from the executed before destroying the network used from the
host side. The following arguments are passed to the host side.
script: container name and config section name (net)
Additional arguments depend on the config section
employing a script hook; the following are used by the
network system: execution context (down), network type
(empty/veth/macvlan/phys), Depending on the network
type, other arguments may be passed:
veth/macvlan/phys. And finally (host-sided) device name.
</para> </para>
<para>
In addition to the information available to all hooks. The
following information is provided to the script:
<itemizedlist>
<listitem>
<para>
LXC_HOOK_TYPE: the hook type. This is either 'up' or 'down'.
</para>
</listitem>
<listitem>
<para>
LXC_HOOK_SECTION: the section type 'net'.
</para>
</listitem>
<listitem>
<para>
LXC_NET_TYPE: the network type. This is one of the valid
network types listed here (e.g. 'macvlan', 'veth').
</para>
</listitem>
<listitem>
<para>
LXC_NET_PARENT: the parent device on the host. This is only
set for network types 'mavclan', 'veth', 'phys'.
</para>
</listitem>
<listitem>
<para>
LXC_NET_PEER: the name of the peer device on the host. This is
only set for 'veth' network types. Note that this information
is only available when <option>lxc.hook.version</option> is set
to 1.
</para>
</listitem>
</itemizedlist>
Whether this information is provided in the form of environment
variables or as arguments to the script depends on the value of
<option>lxc.hook.version</option>. If set to 1 then information is
provided in the form of environment variables. If set to 0
information is provided as arguments to the script.
</para>
<para> <para>
Standard output from the script is logged at debug level. Standard output from the script is logged at debug level.
Standard error is not logged, but can be captured by the Standard error is not logged, but can be captured by the
@ -1636,9 +1717,10 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
at various times in a container's lifetime. at various times in a container's lifetime.
</para> </para>
<para> <para>
When a container hook is executed, information is passed both When a container hook is executed, additional information is passed
as command line arguments and through environment variables. along. The <option>lxc.hook.version</option> argument can be used to
The arguments are: determine if the following arguments are passed as command line
arguments or through environment variables. The arguments are:
<itemizedlist> <itemizedlist>
<listitem><para> Container name. </para></listitem> <listitem><para> Container name. </para></listitem>
<listitem><para> Section (always 'lxc'). </para></listitem> <listitem><para> Section (always 'lxc'). </para></listitem>
@ -1652,13 +1734,38 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
</itemizedlist> </itemizedlist>
The following environment variables are set: The following environment variables are set:
<itemizedlist> <itemizedlist>
<listitem><para> LXC_CGNS_AWARE: indicator whether the container is
cgroup namespace aware. </para></listitem>
<listitem><para> LXC_CONFIG_FILE: the path to the container
configuration file. </para></listitem>
<listitem><para> LXC_HOOK_TYPE: the hook type (e.g. 'clone', 'mount',
'pre-mount'). Note that the existence of this environment variable is
conditional on the value of <option>lxc.hook.version</option>. If it
is set to 1 then LXC_HOOK_TYPE will be set.
</para></listitem>
<listitem><para> LXC_HOOK_SECTION: the section type (e.g. 'lxc',
'net'). Note that the existence of this environment variable is
conditional on the value of <option>lxc.hook.version</option>. If it
is set to 1 then LXC_HOOK_SECTION will be set.
</para></listitem>
<listitem><para> LXC_HOOK_VERSION: the version of the hooks. This
value is identical to the value of the container's
<option>lxc.hook.version</option> config item. If it is set to 0 then
old-style hooks are used. If it is set to 1 then new-style hooks are
used. </para></listitem>
<listitem><para> LXC_LOG_LEVEL: the container's log level. </para></listitem>
<listitem><para> LXC_NAME: is the container's name. </para></listitem> <listitem><para> LXC_NAME: is the container's name. </para></listitem>
<listitem><para> LXC_[NAMESPACE IDENTIFIER]_NS: path under
/proc/PID/fd/ to a file descriptor referring to the container's
namespace. For each preserved namespace type there will be a separate
environment variable. These environment variables will only be set if
<option>lxc.hook.version</option> is set to 1. </para></listitem>
<listitem><para> LXC_ROOTFS_MOUNT: the path to the mounted root filesystem. </para></listitem> <listitem><para> LXC_ROOTFS_MOUNT: the path to the mounted root filesystem. </para></listitem>
<listitem><para> LXC_CONFIG_FILE: the path to the container configuration file. </para></listitem> <listitem><para> LXC_ROOTFS_PATH: this is the lxc.rootfs.path entry
<listitem><para> LXC_SRC_NAME: in the case of the clone hook, this is the original container's name. </para></listitem> for the container. Note this is likely not where the mounted rootfs is
<listitem><para> LXC_ROOTFS_PATH: this is the lxc.rootfs.path entry for the container. Note this is likely not where the mounted rootfs is to be found, use LXC_ROOTFS_MOUNT for that. </para></listitem> to be found, use LXC_ROOTFS_MOUNT for that. </para></listitem>
<listitem><para> LXC_CGNS_AWARE: indicated whether the container is cgroup namespace aware. </para></listitem> <listitem><para> LXC_SRC_NAME: in the case of the clone hook, this is
<listitem><para> LXC_LOG_LEVEL: the container's log level. </para></listitem> the original container's name. </para></listitem>
</itemizedlist> </itemizedlist>
</para> </para>
<para> <para>
@ -1666,6 +1773,33 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Standard error is not logged, but can be captured by the Standard error is not logged, but can be captured by the
hook redirecting its standard error to standard output. hook redirecting its standard error to standard output.
</para> </para>
<variablelist>
<varlistentry>
<term>
<option>lxc.hook.version</option>
</term>
<listitem>
<para>
To pass the arguments in new style via environment variables set to
1 otherwise set to 0 to pass them as arguments.
This setting affects all hooks arguments that were traditionally
passed as arguments to the script. Specifically, it affects the
container name, section (e.g. 'lxc', 'net') and hook type (e.g.
'clone', 'mount', 'pre-mount') arguments. If new-style hooks are
used then the arguments will be available as environment variables.
The container name will be set in LXC_NAME. (This is set
independently of the value used for this config item.) The section
will be set in LXC_HOOK_SECTION and the hook type will be set in
LXC_HOOK_TYPE.
It also affects how the paths to file descriptors referring to the
container's namespaces are passed. If set to 1 then for each
namespace a separate environment variable LXC_[NAMESPACE
IDENTIFIER]_NS will be set. If set to 0 then the paths will be
passed as arguments to the stop hook.
</para>
</listitem>
</varlistentry>
</variablelist>
<variablelist> <variablelist>
<varlistentry> <varlistentry>
<term> <term>

View File

@ -308,84 +308,167 @@ static int run_buffer(char *buffer)
f = lxc_popen(buffer); f = lxc_popen(buffer);
if (!f) { if (!f) {
SYSERROR("Failed to popen() %s.", buffer); SYSERROR("Failed to popen() %s", buffer);
return -1; return -1;
} }
output = malloc(LXC_LOG_BUFFER_SIZE); output = malloc(LXC_LOG_BUFFER_SIZE);
if (!output) { if (!output) {
ERROR("Failed to allocate memory for %s.", buffer); ERROR("Failed to allocate memory for %s", buffer);
lxc_pclose(f); lxc_pclose(f);
return -1; return -1;
} }
while (fgets(output, LXC_LOG_BUFFER_SIZE, f->f)) while (fgets(output, LXC_LOG_BUFFER_SIZE, f->f))
DEBUG("Script %s with output: %s.", buffer, output); DEBUG("Script %s with output: %s", buffer, output);
free(output); free(output);
ret = lxc_pclose(f); ret = lxc_pclose(f);
if (ret == -1) { if (ret == -1) {
SYSERROR("Script exited with error."); SYSERROR("Script exited with error");
return -1; return -1;
} else if (WIFEXITED(ret) && WEXITSTATUS(ret) != 0) { } else if (WIFEXITED(ret) && WEXITSTATUS(ret) != 0) {
ERROR("Script exited with status %d.", WEXITSTATUS(ret)); ERROR("Script exited with status %d", WEXITSTATUS(ret));
return -1; return -1;
} else if (WIFSIGNALED(ret)) { } else if (WIFSIGNALED(ret)) {
ERROR("Script terminated by signal %d.", WTERMSIG(ret)); ERROR("Script terminated by signal %d", WTERMSIG(ret));
return -1; return -1;
} }
return 0; return 0;
} }
static int run_script_argv(const char *name, const char *section, int run_script_argv(const char *name, unsigned int hook_version,
const char *script, const char *hook, const char *section, const char *script,
const char *lxcpath, char **argsin) const char *hookname, char **argsin)
{ {
int ret, i; int buf_pos, i, ret;
char *buffer; char *buffer;
size_t size = 0; size_t size = 0;
INFO("Executing script \"%s\" for container \"%s\", config section \"%s\".", if (hook_version == 0)
script, name, section); INFO("Executing script \"%s\" for container \"%s\", config "
"section \"%s\"", script, name, section);
else
INFO("Executing script \"%s\" for container \"%s\"", script, name);
for (i = 0; argsin && argsin[i]; i++) for (i = 0; argsin && argsin[i]; i++)
size += strlen(argsin[i]) + 1; size += strlen(argsin[i]) + 1;
size += strlen(hook) + 1; size += sizeof("exec");
size += strlen("exec");
size += strlen(script); size += strlen(script);
size += strlen(name); size++;
size += strlen(section);
size += 4;
if (size > INT_MAX) if (size > INT_MAX)
return -1; return -EFBIG;
buffer = alloca(size); buffer = alloca(size);
if (!buffer) {
ERROR("Failed to allocate memory.");
return -1;
}
ret = if (hook_version == 0) {
snprintf(buffer, size, "exec %s %s %s %s", script, name, section, hook); size += strlen(hookname);
if (ret < 0 || (size_t)ret >= size) { size++;
ERROR("Script name too long.");
return -1; size += strlen(name);
size++;
size += strlen(section);
size++;
if (size > INT_MAX)
return -EFBIG;
buf_pos = snprintf(buffer, size, "exec %s %s %s %s", script, name, section, hookname);
if (buf_pos < 0 || (size_t)buf_pos >= size) {
ERROR("Failed to create command line for script \"%s\"", script);
return -1;
}
} else {
buf_pos = snprintf(buffer, size, "exec %s", script);
if (buf_pos < 0 || (size_t)buf_pos >= size) {
ERROR("Failed to create command line for script \"%s\"", script);
return -1;
}
ret = setenv("LXC_HOOK_TYPE", hookname, 1);
if (ret < 0) {
SYSERROR("Failed to set environment variable: "
"LXC_HOOK_TYPE=%s", hookname);
return -1;
}
TRACE("Set environment variable: LXC_HOOK_TYPE=%s", section);
ret = setenv("LXC_HOOK_SECTION", section, 1);
if (ret < 0) {
SYSERROR("Failed to set environment variable: "
"LXC_HOOK_SECTION=%s", section);
return -1;
}
TRACE("Set environment variable: LXC_HOOK_SECTION=%s", section);
if (strcmp(section, "net") == 0) {
char *parent;
if (!argsin[0])
return -EINVAL;
ret = setenv("LXC_NET_TYPE", argsin[0], 1);
if (ret < 0) {
SYSERROR("Failed to set environment variable: "
"LXC_NET_TYPE=%s", argsin[0]);
return -1;
}
TRACE("Set environment variable: LXC_NET_TYPE=%s", argsin[0]);
parent = argsin[1] ? argsin[1] : "";
if (strcmp(argsin[0], "macvlan")) {
ret = setenv("LXC_NET_PARENT", parent, 1);
if (ret < 0) {
SYSERROR("Failed to set environment "
"variable: LXC_NET_PARENT=%s", parent);
return -1;
}
TRACE("Set environment variable: LXC_NET_PARENT=%s", parent);
} else if (strcmp(argsin[0], "phys")) {
ret = setenv("LXC_NET_PARENT", parent, 1);
if (ret < 0) {
SYSERROR("Failed to set environment "
"variable: LXC_NET_PARENT=%s", parent);
return -1;
}
TRACE("Set environment variable: LXC_NET_PARENT=%s", parent);
} else if (strcmp(argsin[0], "veth")) {
char *peer = argsin[2] ? argsin[2] : "";
ret = setenv("LXC_NET_PEER", peer, 1);
if (ret < 0) {
SYSERROR("Failed to set environment "
"variable: LXC_NET_PEER=%s", peer);
return -1;
}
TRACE("Set environment variable: LXC_NET_PEER=%s", peer);
ret = setenv("LXC_NET_PARENT", parent, 1);
if (ret < 0) {
SYSERROR("Failed to set environment "
"variable: LXC_NET_PARENT=%s", parent);
return -1;
}
TRACE("Set environment variable: LXC_NET_PARENT=%s", parent);
}
}
} }
for (i = 0; argsin && argsin[i]; i++) { for (i = 0; argsin && argsin[i]; i++) {
int len = size - ret; size_t len = size - buf_pos;
int rc;
rc = snprintf(buffer + ret, len, " %s", argsin[i]); ret = snprintf(buffer + buf_pos, len, " %s", argsin[i]);
if (rc < 0 || rc >= len) { if (ret < 0 || (size_t)ret >= len) {
ERROR("Script args too long."); ERROR("Failed to create command line for script \"%s\"", script);
return -1; return -1;
} }
ret += rc; buf_pos += ret;
} }
return run_buffer(buffer); return run_buffer(buffer);
@ -2484,6 +2567,7 @@ struct lxc_conf *lxc_conf_init(void)
lxc_list_init(&new->limits); lxc_list_init(&new->limits);
lxc_list_init(&new->sysctls); lxc_list_init(&new->sysctls);
lxc_list_init(&new->procs); lxc_list_init(&new->procs);
new->hooks_version = 0;
for (i = 0; i < NUM_LXC_HOOKS; i++) for (i = 0; i < NUM_LXC_HOOKS; i++)
lxc_list_init(&new->hooks[i]); lxc_list_init(&new->hooks[i]);
lxc_list_init(&new->groups); lxc_list_init(&new->groups);
@ -3068,7 +3152,7 @@ int do_rootfs_setup(struct lxc_conf *conf, const char *name, const char *lxcpath
remount_all_slave(); remount_all_slave();
if (run_lxc_hooks(name, "pre-mount", conf, lxcpath, NULL)) { if (run_lxc_hooks(name, "pre-mount", conf, NULL)) {
ERROR("failed to run pre-mount hooks for container '%s'.", name); ERROR("failed to run pre-mount hooks for container '%s'.", name);
return -1; return -1;
} }
@ -3177,13 +3261,13 @@ int lxc_setup(struct lxc_handler *handler)
return -1; return -1;
} }
if (run_lxc_hooks(name, "mount", lxc_conf, lxcpath, NULL)) { if (run_lxc_hooks(name, "mount", lxc_conf, NULL)) {
ERROR("failed to run mount hooks for container '%s'.", name); ERROR("failed to run mount hooks for container '%s'.", name);
return -1; return -1;
} }
if (lxc_conf->autodev > 0) { if (lxc_conf->autodev > 0) {
if (run_lxc_hooks(name, "autodev", lxc_conf, lxcpath, NULL)) { if (run_lxc_hooks(name, "autodev", lxc_conf, NULL)) {
ERROR("failed to run autodev hooks for container '%s'.", name); ERROR("failed to run autodev hooks for container '%s'.", name);
return -1; return -1;
} }
@ -3260,41 +3344,45 @@ int lxc_setup(struct lxc_handler *handler)
return 0; return 0;
} }
int run_lxc_hooks(const char *name, char *hook, struct lxc_conf *conf, int run_lxc_hooks(const char *name, char *hookname, struct lxc_conf *conf,
const char *lxcpath, char *argv[]) char *argv[])
{ {
int which = -1;
struct lxc_list *it; struct lxc_list *it;
int which = -1;
if (strcmp(hook, "pre-start") == 0) if (strcmp(hookname, "pre-start") == 0)
which = LXCHOOK_PRESTART; which = LXCHOOK_PRESTART;
else if (strcmp(hook, "start-host") == 0) else if (strcmp(hookname, "start-host") == 0)
which = LXCHOOK_START_HOST; which = LXCHOOK_START_HOST;
else if (strcmp(hook, "pre-mount") == 0) else if (strcmp(hookname, "pre-mount") == 0)
which = LXCHOOK_PREMOUNT; which = LXCHOOK_PREMOUNT;
else if (strcmp(hook, "mount") == 0) else if (strcmp(hookname, "mount") == 0)
which = LXCHOOK_MOUNT; which = LXCHOOK_MOUNT;
else if (strcmp(hook, "autodev") == 0) else if (strcmp(hookname, "autodev") == 0)
which = LXCHOOK_AUTODEV; which = LXCHOOK_AUTODEV;
else if (strcmp(hook, "start") == 0) else if (strcmp(hookname, "start") == 0)
which = LXCHOOK_START; which = LXCHOOK_START;
else if (strcmp(hook, "stop") == 0) else if (strcmp(hookname, "stop") == 0)
which = LXCHOOK_STOP; which = LXCHOOK_STOP;
else if (strcmp(hook, "post-stop") == 0) else if (strcmp(hookname, "post-stop") == 0)
which = LXCHOOK_POSTSTOP; which = LXCHOOK_POSTSTOP;
else if (strcmp(hook, "clone") == 0) else if (strcmp(hookname, "clone") == 0)
which = LXCHOOK_CLONE; which = LXCHOOK_CLONE;
else if (strcmp(hook, "destroy") == 0) else if (strcmp(hookname, "destroy") == 0)
which = LXCHOOK_DESTROY; which = LXCHOOK_DESTROY;
else else
return -1; return -1;
lxc_list_for_each(it, &conf->hooks[which]) { lxc_list_for_each(it, &conf->hooks[which]) {
int ret; int ret;
char *hookname = it->elem; char *hook = it->elem;
ret = run_script_argv(name, "lxc", hookname, hook, lxcpath, argv);
if (ret) ret = run_script_argv(name, conf->hooks_version, "lxc", hook,
return ret; hookname, argv);
if (ret < 0)
return -1;
} }
return 0; return 0;
} }

View File

@ -295,7 +295,11 @@ struct lxc_conf {
struct lxc_rootfs rootfs; struct lxc_rootfs rootfs;
char *ttydir; char *ttydir;
int close_all_fds; int close_all_fds;
struct lxc_list hooks[NUM_LXC_HOOKS];
struct {
unsigned int hooks_version;
struct lxc_list hooks[NUM_LXC_HOOKS];
};
char *lsm_aa_profile; char *lsm_aa_profile;
unsigned int lsm_aa_allow_incomplete; unsigned int lsm_aa_allow_incomplete;
@ -408,7 +412,7 @@ extern struct lxc_conf *current_config;
#endif #endif
extern int run_lxc_hooks(const char *name, char *hook, struct lxc_conf *conf, extern int run_lxc_hooks(const char *name, char *hook, struct lxc_conf *conf,
const char *lxcpath, char *argv[]); char *argv[]);
extern int detect_shared_rootfs(void); extern int detect_shared_rootfs(void);
extern struct lxc_conf *lxc_conf_init(void); extern struct lxc_conf *lxc_conf_init(void);
extern void lxc_conf_free(struct lxc_conf *conf); extern void lxc_conf_free(struct lxc_conf *conf);
@ -453,6 +457,9 @@ extern unsigned long add_required_remount_flags(const char *s, const char *d,
unsigned long flags); unsigned long flags);
extern int run_script(const char *name, const char *section, const char *script, extern int run_script(const char *name, const char *section, const char *script,
...); ...);
extern int run_script_argv(const char *name, unsigned int hook_version,
const char *section, const char *script,
const char *hookname, char **argsin);
extern int in_caplist(int cap, struct lxc_list *caps); extern int in_caplist(int cap, struct lxc_list *caps);
extern int setup_sysctl_parameters(struct lxc_list *sysctls); extern int setup_sysctl_parameters(struct lxc_list *sysctls);
extern int lxc_clear_sysctls(struct lxc_conf *c, const char *key); extern int lxc_clear_sysctls(struct lxc_conf *c, const char *key);

View File

@ -92,6 +92,7 @@ lxc_config_define(ephemeral);
lxc_config_define(execute_cmd); lxc_config_define(execute_cmd);
lxc_config_define(group); lxc_config_define(group);
lxc_config_define(hooks); lxc_config_define(hooks);
lxc_config_define(hooks_version);
lxc_config_define(idmaps); lxc_config_define(idmaps);
lxc_config_define(includefiles); lxc_config_define(includefiles);
lxc_config_define(init_cmd); lxc_config_define(init_cmd);
@ -168,11 +169,12 @@ static struct lxc_config_t config[] = {
{ "lxc.hook.destroy", false, set_config_hooks, get_config_hooks, clr_config_hooks, }, { "lxc.hook.destroy", false, set_config_hooks, get_config_hooks, clr_config_hooks, },
{ "lxc.hook.mount", false, set_config_hooks, get_config_hooks, clr_config_hooks, }, { "lxc.hook.mount", false, set_config_hooks, get_config_hooks, clr_config_hooks, },
{ "lxc.hook.post-stop", false, set_config_hooks, get_config_hooks, clr_config_hooks, }, { "lxc.hook.post-stop", false, set_config_hooks, get_config_hooks, clr_config_hooks, },
{ "lxc.hook.start-host", false, set_config_hooks, get_config_hooks, clr_config_hooks, },
{ "lxc.hook.pre-start", false, set_config_hooks, get_config_hooks, clr_config_hooks, },
{ "lxc.hook.pre-mount", false, set_config_hooks, get_config_hooks, clr_config_hooks, }, { "lxc.hook.pre-mount", false, set_config_hooks, get_config_hooks, clr_config_hooks, },
{ "lxc.hook.pre-start", false, set_config_hooks, get_config_hooks, clr_config_hooks, },
{ "lxc.hook.start", false, set_config_hooks, get_config_hooks, clr_config_hooks, }, { "lxc.hook.start", false, set_config_hooks, get_config_hooks, clr_config_hooks, },
{ "lxc.hook.start-host", false, set_config_hooks, get_config_hooks, clr_config_hooks, },
{ "lxc.hook.stop", false, set_config_hooks, get_config_hooks, clr_config_hooks, }, { "lxc.hook.stop", false, set_config_hooks, get_config_hooks, clr_config_hooks, },
{ "lxc.hook.version", false, set_config_hooks_version, get_config_hooks_version, clr_config_hooks_version, },
{ "lxc.hook", false, set_config_hooks, get_config_hooks, clr_config_hooks, }, { "lxc.hook", false, set_config_hooks, get_config_hooks, clr_config_hooks, },
{ "lxc.idmap", false, set_config_idmaps, get_config_idmaps, clr_config_idmaps, }, { "lxc.idmap", false, set_config_idmaps, get_config_idmaps, clr_config_idmaps, },
{ "lxc.include", false, set_config_includefiles, get_config_includefiles, clr_config_includefiles, }, { "lxc.include", false, set_config_includefiles, get_config_includefiles, clr_config_includefiles, },
@ -980,6 +982,29 @@ static int set_config_hooks(const char *key, const char *value,
return -1; return -1;
} }
static int set_config_hooks_version(const char *key, const char *value,
struct lxc_conf *lxc_conf, void *data)
{
int ret;
unsigned int tmp;
if (lxc_config_value_empty(value))
return clr_config_hooks_version(key, lxc_conf, NULL);
ret = lxc_safe_uint(value, &tmp);
if (ret < 0)
return -1;
if (tmp > 1) {
ERROR("Invalid hook version specified. Currently only 0 "
"(legacy) and 1 are supported");
return -1;
}
lxc_conf->hooks_version = tmp;
return 0;
}
static int set_config_personality(const char *key, const char *value, static int set_config_personality(const char *key, const char *value,
struct lxc_conf *lxc_conf, void *data) struct lxc_conf *lxc_conf, void *data)
{ {
@ -3154,6 +3179,12 @@ static int get_config_hooks(const char *key, char *retv, int inlen,
return fulllen; return fulllen;
} }
static int get_config_hooks_version(const char *key, char *retv, int inlen,
struct lxc_conf *c, void *data)
{
return lxc_get_conf_int(c, retv, inlen, c->hooks_version);
}
static int get_config_net(const char *key, char *retv, int inlen, static int get_config_net(const char *key, char *retv, int inlen,
struct lxc_conf *c, void *data) struct lxc_conf *c, void *data)
{ {
@ -3688,6 +3719,14 @@ static inline int clr_config_hooks(const char *key, struct lxc_conf *c,
return lxc_clear_hooks(c, key); return lxc_clear_hooks(c, key);
} }
static inline int clr_config_hooks_version(const char *key, struct lxc_conf *c,
void *data)
{
/* default to legacy hooks version */
c->hooks_version = 0;
return 0;
}
static inline int clr_config_net(const char *key, struct lxc_conf *c, static inline int clr_config_net(const char *key, struct lxc_conf *c,
void *data) void *data)
{ {

View File

@ -2746,7 +2746,7 @@ static bool container_destroy(struct lxc_container *c,
SYSERROR("Failed to set environment variable for console log"); SYSERROR("Failed to set environment variable for console log");
/* End of environment variable setup for hooks */ /* End of environment variable setup for hooks */
if (run_lxc_hooks(c->name, "destroy", conf, c->get_config_path(c), NULL)) { if (run_lxc_hooks(c->name, "destroy", conf, NULL)) {
ERROR("Failed to execute clone hook for \"%s\"", c->name); ERROR("Failed to execute clone hook for \"%s\"", c->name);
goto out; goto out;
} }
@ -3458,7 +3458,7 @@ static int clone_update_rootfs(struct clone_update_data *data)
SYSERROR("failed to set environment variable for rootfs mount"); SYSERROR("failed to set environment variable for rootfs mount");
} }
if (run_lxc_hooks(c->name, "clone", conf, c->get_config_path(c), hookargs)) { if (run_lxc_hooks(c->name, "clone", conf, hookargs)) {
ERROR("Error executing clone hook for %s", c->name); ERROR("Error executing clone hook for %s", c->name);
storage_put(bdev); storage_put(bdev);
return -1; return -1;

View File

@ -87,13 +87,13 @@ pid_t lxc_clone(int (*fn)(void *), void *arg, int flags)
* linux/fs/namespace.c:mntns_install(). * linux/fs/namespace.c:mntns_install().
*/ */
const struct ns_info ns_info[LXC_NS_MAX] = { const struct ns_info ns_info[LXC_NS_MAX] = {
[LXC_NS_USER] = {"user", CLONE_NEWUSER, "CLONE_NEWUSER"}, [LXC_NS_USER] = { "user", CLONE_NEWUSER, "CLONE_NEWUSER", "LXC_USER_NS" },
[LXC_NS_MNT] = {"mnt", CLONE_NEWNS, "CLONE_NEWNS"}, [LXC_NS_MNT] = { "mnt", CLONE_NEWNS, "CLONE_NEWNS", "LXC_MNT_NS" },
[LXC_NS_PID] = {"pid", CLONE_NEWPID, "CLONE_NEWPID"}, [LXC_NS_PID] = { "pid", CLONE_NEWPID, "CLONE_NEWPID", "LXC_PID_NS" },
[LXC_NS_UTS] = {"uts", CLONE_NEWUTS, "CLONE_NEWUTS"}, [LXC_NS_UTS] = { "uts", CLONE_NEWUTS, "CLONE_NEWUTS", "LXC_UTS_NS" },
[LXC_NS_IPC] = {"ipc", CLONE_NEWIPC, "CLONE_NEWIPC"}, [LXC_NS_IPC] = { "ipc", CLONE_NEWIPC, "CLONE_NEWIPC", "LXC_IPC_NS" },
[LXC_NS_NET] = {"net", CLONE_NEWNET, "CLONE_NEWNET"}, [LXC_NS_NET] = { "net", CLONE_NEWNET, "CLONE_NEWNET", "LXC_NET_NS" },
[LXC_NS_CGROUP] = {"cgroup", CLONE_NEWCGROUP, "CLONE_NEWCGROUP"} [LXC_NS_CGROUP] = { "cgroup", CLONE_NEWCGROUP, "CLONE_NEWCGROUP", "LXC_CGROUP_NS" }
}; };
int lxc_namespace_2_cloneflag(const char *namespace) int lxc_namespace_2_cloneflag(const char *namespace)

View File

@ -68,6 +68,7 @@ extern const struct ns_info {
const char *proc_name; const char *proc_name;
int clone_flag; int clone_flag;
const char *flag_name; const char *flag_name;
const char *env_name;
} ns_info[LXC_NS_MAX]; } ns_info[LXC_NS_MAX];
#if defined(__ia64__) #if defined(__ia64__)

View File

@ -204,9 +204,17 @@ static int instantiate_veth(struct lxc_handler *handler, struct lxc_netdev *netd
} }
if (netdev->upscript) { if (netdev->upscript) {
err = run_script(handler->name, "net", netdev->upscript, "up", char *argv[] = {
"veth", veth1, (char*) NULL); "veth",
if (err) netdev->link,
veth1,
NULL,
};
err = run_script_argv(handler->name,
handler->conf->hooks_version, "net",
netdev->upscript, "up", argv);
if (err < 0)
goto out_delete; goto out_delete;
} }
@ -254,9 +262,16 @@ static int instantiate_macvlan(struct lxc_handler *handler, struct lxc_netdev *n
} }
if (netdev->upscript) { if (netdev->upscript) {
err = run_script(handler->name, "net", netdev->upscript, "up", char *argv[] = {
"macvlan", netdev->link, (char*) NULL); "macvlan",
if (err) netdev->link,
NULL,
};
err = run_script_argv(handler->name,
handler->conf->hooks_version, "net",
netdev->upscript, "up", argv);
if (err < 0)
goto on_error; goto on_error;
} }
@ -323,6 +338,13 @@ static int instantiate_vlan(struct lxc_handler *handler, struct lxc_netdev *netd
static int instantiate_phys(struct lxc_handler *handler, struct lxc_netdev *netdev) static int instantiate_phys(struct lxc_handler *handler, struct lxc_netdev *netdev)
{ {
int ret;
char *argv[] = {
"phys",
netdev->link,
NULL,
};
if (netdev->link[0] == '\0') { if (netdev->link[0] == '\0') {
ERROR("No link for physical interface specified"); ERROR("No link for physical interface specified");
return -1; return -1;
@ -346,27 +368,34 @@ static int instantiate_phys(struct lxc_handler *handler, struct lxc_netdev *netd
*/ */
netdev->priv.phys_attr.ifindex = netdev->ifindex; netdev->priv.phys_attr.ifindex = netdev->ifindex;
if (netdev->upscript) { if (!netdev->upscript)
int err; return 0;
err = run_script(handler->name, "net", netdev->upscript,
"up", "phys", netdev->link, (char*) NULL); ret = run_script_argv(handler->name, handler->conf->hooks_version,
if (err) "net", netdev->upscript, "up", argv);
return -1; if (ret < 0)
} return -1;
return 0; return 0;
} }
static int instantiate_empty(struct lxc_handler *handler, struct lxc_netdev *netdev) static int instantiate_empty(struct lxc_handler *handler, struct lxc_netdev *netdev)
{ {
int ret;
char *argv[] = {
"empty",
NULL,
};
netdev->ifindex = 0; netdev->ifindex = 0;
if (netdev->upscript) { if (!netdev->upscript)
int err; return 0;
err = run_script(handler->name, "net", netdev->upscript,
"up", "empty", (char*) NULL); ret = run_script_argv(handler->name, handler->conf->hooks_version,
if (err) "net", netdev->upscript, "up", argv);
return -1; if (ret < 0)
} return -1;
return 0; return 0;
} }
@ -387,34 +416,48 @@ static instantiate_cb netdev_conf[LXC_NET_MAXCONFTYPE + 1] = {
static int shutdown_veth(struct lxc_handler *handler, struct lxc_netdev *netdev) static int shutdown_veth(struct lxc_handler *handler, struct lxc_netdev *netdev)
{ {
char *veth1; int ret;
int err; char *argv[] = {
"veth",
netdev->link,
NULL,
NULL,
};
if (!netdev->downscript)
return 0;
if (netdev->priv.veth_attr.pair[0] != '\0') if (netdev->priv.veth_attr.pair[0] != '\0')
veth1 = netdev->priv.veth_attr.pair; argv[2] = netdev->priv.veth_attr.pair;
else else
veth1 = netdev->priv.veth_attr.veth1; argv[2] = netdev->priv.veth_attr.veth1;
ret = run_script_argv(handler->name,
handler->conf->hooks_version, "net",
netdev->downscript, "down", argv);
if (ret < 0)
return -1;
if (netdev->downscript) {
err = run_script(handler->name, "net", netdev->downscript,
"down", "veth", veth1, (char*) NULL);
if (err)
return -1;
}
return 0; return 0;
} }
static int shutdown_macvlan(struct lxc_handler *handler, struct lxc_netdev *netdev) static int shutdown_macvlan(struct lxc_handler *handler, struct lxc_netdev *netdev)
{ {
int err; int ret;
char *argv[] = {
"macvlan",
netdev->link,
NULL,
};
if (!netdev->downscript)
return 0;
ret = run_script_argv(handler->name, handler->conf->hooks_version,
"net", netdev->downscript, "down", argv);
if (ret < 0)
return -1;
if (netdev->downscript) {
err = run_script(handler->name, "net", netdev->downscript,
"down", "macvlan", netdev->link,
(char*) NULL);
if (err)
return -1;
}
return 0; return 0;
} }
@ -425,27 +468,40 @@ static int shutdown_vlan(struct lxc_handler *handler, struct lxc_netdev *netdev)
static int shutdown_phys(struct lxc_handler *handler, struct lxc_netdev *netdev) static int shutdown_phys(struct lxc_handler *handler, struct lxc_netdev *netdev)
{ {
int err; int ret;
char *argv[] = {
"phys",
netdev->link,
NULL,
};
if (!netdev->downscript)
return 0;
ret = run_script_argv(handler->name, handler->conf->hooks_version,
"net", netdev->downscript, "down", argv);
if (ret < 0)
return -1;
if (netdev->downscript) {
err = run_script(handler->name, "net", netdev->downscript,
"down", "phys", netdev->link, (char*) NULL);
if (err)
return -1;
}
return 0; return 0;
} }
static int shutdown_empty(struct lxc_handler *handler, struct lxc_netdev *netdev) static int shutdown_empty(struct lxc_handler *handler, struct lxc_netdev *netdev)
{ {
int err; int ret;
char *argv[] = {
"empty",
NULL,
};
if (!netdev->downscript)
return 0;
ret = run_script_argv(handler->name, handler->conf->hooks_version,
"net", netdev->downscript, "down", argv);
if (ret < 0)
return -1;
if (netdev->downscript) {
err = run_script(handler->name, "net", netdev->downscript,
"down", "empty", (char*) NULL);
if (err)
return -1;
}
return 0; return 0;
} }

View File

@ -612,6 +612,7 @@ on_error:
int lxc_init(const char *name, struct lxc_handler *handler) int lxc_init(const char *name, struct lxc_handler *handler)
{ {
int ret;
const char *loglevel; const char *loglevel;
struct lxc_conf *conf = handler->conf; struct lxc_conf *conf = handler->conf;
@ -656,11 +657,18 @@ int lxc_init(const char *name, struct lxc_handler *handler)
loglevel = lxc_log_priority_to_string(lxc_log_get_level()); loglevel = lxc_log_priority_to_string(lxc_log_get_level());
if (setenv("LXC_LOG_LEVEL", loglevel, 1)) if (setenv("LXC_LOG_LEVEL", loglevel, 1))
SYSERROR("Failed to set environment variable LXC_LOG_LEVEL=%s", loglevel); SYSERROR("Failed to set environment variable LXC_LOG_LEVEL=%s", loglevel);
if (conf->hooks_version == 0)
ret = setenv("LXC_HOOK_VERSION", "0", 1);
else
ret = setenv("LXC_HOOK_VERSION", "1", 1);
if (ret < 0)
SYSERROR("Failed to set environment variable LXC_HOOK_VERSION=%u", conf->hooks_version);
/* End of environment variable setup for hooks. */ /* End of environment variable setup for hooks. */
TRACE("set environment variables"); TRACE("set environment variables");
if (run_lxc_hooks(name, "pre-start", conf, handler->lxcpath, NULL)) { if (run_lxc_hooks(name, "pre-start", conf, NULL)) {
ERROR("Failed to run lxc.hook.pre-start for container \"%s\".", name); ERROR("Failed to run lxc.hook.pre-start for container \"%s\".", name);
goto out_aborting; goto out_aborting;
} }
@ -708,8 +716,8 @@ out_close_maincmd_fd:
void lxc_fini(const char *name, struct lxc_handler *handler) void lxc_fini(const char *name, struct lxc_handler *handler)
{ {
int i, rc; int i, rc;
pid_t self;
struct lxc_list *cur, *next; struct lxc_list *cur, *next;
pid_t self = getpid();
char *namespaces[LXC_NS_MAX + 1]; char *namespaces[LXC_NS_MAX + 1];
size_t namespace_count = 0; size_t namespace_count = 0;
@ -718,16 +726,37 @@ void lxc_fini(const char *name, struct lxc_handler *handler)
*/ */
lxc_set_state(name, handler, STOPPING); lxc_set_state(name, handler, STOPPING);
self = getpid();
for (i = 0; i < LXC_NS_MAX; i++) { for (i = 0; i < LXC_NS_MAX; i++) {
if (handler->nsfd[i] != -1) { if (handler->nsfd[i] < 0)
rc = asprintf(&namespaces[namespace_count], "%s:/proc/%d/fd/%d", continue;
ns_info[i].proc_name, self, handler->nsfd[i]);
if (rc == -1) { if (handler->conf->hooks_version == 0)
SYSERROR("Failed to allocate memory."); rc = asprintf(&namespaces[namespace_count],
break; "%s:/proc/%d/fd/%d", ns_info[i].proc_name,
} self, handler->nsfd[i]);
++namespace_count; else
rc = asprintf(&namespaces[namespace_count],
"/proc/%d/fd/%d", self, handler->nsfd[i]);
if (rc == -1) {
SYSERROR("Failed to allocate memory.");
break;
} }
if (handler->conf->hooks_version == 0) {
namespace_count++;
continue;
}
rc = setenv(ns_info[i].env_name, namespaces[namespace_count], 1);
if (rc < 0)
SYSERROR("Failed to set environment variable %s=%s",
ns_info[i].env_name, namespaces[namespace_count]);
else
TRACE("Set environment variable %s=%s",
ns_info[i].env_name, namespaces[namespace_count]);
namespace_count++;
} }
namespaces[namespace_count] = NULL; namespaces[namespace_count] = NULL;
@ -737,8 +766,10 @@ void lxc_fini(const char *name, struct lxc_handler *handler)
if (!handler->conf->reboot && setenv("LXC_TARGET", "stop", 1)) if (!handler->conf->reboot && setenv("LXC_TARGET", "stop", 1))
SYSERROR("Failed to set environment variable: LXC_TARGET=stop."); SYSERROR("Failed to set environment variable: LXC_TARGET=stop.");
if (run_lxc_hooks(name, "stop", handler->conf, handler->lxcpath, namespaces)) if (handler->conf->hooks_version == 0)
ERROR("Failed to run lxc.hook.stop for container \"%s\".", name); rc = run_lxc_hooks(name, "stop", handler->conf, namespaces);
else
rc = run_lxc_hooks(name, "stop", handler->conf, NULL);
while (namespace_count--) while (namespace_count--)
free(namespaces[namespace_count]); free(namespaces[namespace_count]);
@ -761,7 +792,7 @@ void lxc_fini(const char *name, struct lxc_handler *handler)
handler->conf->maincmd_fd = -1; handler->conf->maincmd_fd = -1;
} }
if (run_lxc_hooks(name, "post-stop", handler->conf, handler->lxcpath, NULL)) { if (run_lxc_hooks(name, "post-stop", handler->conf, NULL)) {
ERROR("Failed to run lxc.hook.post-stop for container \"%s\".", name); ERROR("Failed to run lxc.hook.post-stop for container \"%s\".", name);
if (handler->conf->reboot) { if (handler->conf->reboot) {
WARN("Container will be stopped instead of rebooted."); WARN("Container will be stopped instead of rebooted.");
@ -1022,7 +1053,7 @@ static int do_start(void *data)
if (lxc_seccomp_load(handler->conf) != 0) if (lxc_seccomp_load(handler->conf) != 0)
goto out_warn_father; goto out_warn_father;
if (run_lxc_hooks(handler->name, "start", handler->conf, handler->lxcpath, NULL)) { if (run_lxc_hooks(handler->name, "start", handler->conf, NULL)) {
ERROR("Failed to run lxc.hook.start for container \"%s\".", handler->name); ERROR("Failed to run lxc.hook.start for container \"%s\".", handler->name);
goto out_warn_father; goto out_warn_father;
} }
@ -1482,7 +1513,7 @@ static int lxc_spawn(struct lxc_handler *handler)
SYSERROR("Failed to set environment variable: LXC_PID=%s.", pidstr); SYSERROR("Failed to set environment variable: LXC_PID=%s.", pidstr);
/* Run any host-side start hooks */ /* Run any host-side start hooks */
if (run_lxc_hooks(name, "start-host", conf, handler->lxcpath, NULL)) { if (run_lxc_hooks(name, "start-host", conf, NULL)) {
ERROR("Failed to run lxc.hook.start-host for container \"%s\".", name); ERROR("Failed to run lxc.hook.start-host for container \"%s\".", name);
return -1; return -1;
} }

View File

@ -301,16 +301,16 @@ static int set_get_compare_clear_save_load_network(
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
int ret;
struct lxc_container *c; struct lxc_container *c;
int fd = -1; int fd = -1, fret = EXIT_FAILURE;
int ret = EXIT_FAILURE;
char tmpf[] = "lxc-parse-config-file-XXXXXX"; char tmpf[] = "lxc-parse-config-file-XXXXXX";
char retval[4096] = {0}; char retval[4096] = {0};
fd = mkstemp(tmpf); fd = mkstemp(tmpf);
if (fd < 0) { if (fd < 0) {
lxc_error("%s\n", "Could not create temporary file"); lxc_error("%s\n", "Could not create temporary file");
exit(ret); exit(fret);
} }
close(fd); close(fd);
@ -1110,10 +1110,23 @@ int main(int argc, char *argv[])
goto non_test_error; goto non_test_error;
} }
ret = EXIT_SUCCESS; ret = set_get_compare_clear_save_load(c, "lxc.hook.version", "1", tmpf, true);
if (ret < 0) {
lxc_error("%s\n", "lxc.hook.version");
goto non_test_error;
}
ret = set_get_compare_clear_save_load(c, "lxc.hook.version", "2", tmpf, true);
if (ret == 0) {
lxc_error("%s\n", "lxc.hook.version");
goto non_test_error;
}
fret = EXIT_SUCCESS;
non_test_error: non_test_error:
(void)unlink(tmpf); (void)unlink(tmpf);
(void)rmdir(dirname(c->configfile)); (void)rmdir(dirname(c->configfile));
lxc_container_put(c); lxc_container_put(c);
exit(ret); exit(fret);
} }