mirror of
git://github.com/lxc/lxc
synced 2025-08-29 20:29:51 +00:00
lxc: add clone hook.
Add a clone hook called from api_clone. Pass arguments to it from lxc_clone.c. The clone update hook is called while the container's bdev is mounted. Information about the container is passed in through environment variables LXC_ROOTFS_PATH, LXC_NAME, The LXC_ROOTFS_MOUNT, and LXC_CONFIG_FILE. LXC_ROOTFS_MOUNT=/usr/lib/x86_64-linux-gnu/lxc LXC_CONFIG_FILE=/var/lib/lxc/demo3/config LXC_ROOTFS_PATH=/var/lib/lxc/demo3/rootfs LXC_NAME=demo3 So from the hook, updates to the container should be made under $LXC_ROOTFS_MOUNT/ . The hook also receives command line arguments as follows: First argument is container name, second is always 'lxc', third is the hook name (always clone), then come the arguments which were passed to lxc-clone. I.e. when I did: sudo lxc-clone demo2 demo3 -- hey there dude the arguments passed in were "demo3 lxc clone hey there dude" I personally would like to drop the first two arguments. The name is available as $LXC_NAME, and the section argument ('lxc') is meaningless. However, doing so risks invalidating existing hooks. Soon analogous create and destroy hooks will be added as well. Signed-off-by: Serge Hallyn <serge.hallyn@ubuntu.com> Acked-by: Stéphane Graber <stgraber@ubuntu.com>
This commit is contained in:
parent
9a93d99213
commit
148e91f567
@ -173,7 +173,7 @@ return -1;
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
char *lxchook_names[NUM_LXC_HOOKS] = {
|
char *lxchook_names[NUM_LXC_HOOKS] = {
|
||||||
"pre-start", "pre-mount", "mount", "autodev", "start", "post-stop" };
|
"pre-start", "pre-mount", "mount", "autodev", "start", "post-stop", "clone" };
|
||||||
|
|
||||||
typedef int (*instanciate_cb)(struct lxc_handler *, struct lxc_netdev *);
|
typedef int (*instanciate_cb)(struct lxc_handler *, struct lxc_netdev *);
|
||||||
|
|
||||||
@ -336,6 +336,55 @@ static int run_buffer(char *buffer)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int run_script_argv(const char *name, const char *section,
|
||||||
|
const char *script, const char *hook, char **argsin)
|
||||||
|
{
|
||||||
|
int ret, i;
|
||||||
|
char *buffer;
|
||||||
|
size_t size = 0;
|
||||||
|
|
||||||
|
INFO("Executing script '%s' for container '%s', config section '%s'",
|
||||||
|
script, name, section);
|
||||||
|
|
||||||
|
for (i=0; argsin && argsin[i]; i++)
|
||||||
|
size += strlen(argsin[i]) + 1;
|
||||||
|
|
||||||
|
size += strlen(hook) + 1;
|
||||||
|
|
||||||
|
size += strlen(script);
|
||||||
|
size += strlen(name);
|
||||||
|
size += strlen(section);
|
||||||
|
size += 3;
|
||||||
|
|
||||||
|
if (size > INT_MAX)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
buffer = alloca(size);
|
||||||
|
if (!buffer) {
|
||||||
|
ERROR("failed to allocate memory");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = snprintf(buffer, size, "%s %s %s %s", script, name, section, hook);
|
||||||
|
if (ret < 0 || ret >= size) {
|
||||||
|
ERROR("Script name too long");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i=0; argsin && argsin[i]; i++) {
|
||||||
|
int len = size-ret;
|
||||||
|
int rc;
|
||||||
|
rc = snprintf(buffer + ret, len, " %s", argsin[i]);
|
||||||
|
if (rc < 0 || rc >= len) {
|
||||||
|
ERROR("Script args too long");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
ret += rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
return run_buffer(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
static int run_script(const char *name, const char *section,
|
static int run_script(const char *name, const char *section,
|
||||||
const char *script, ...)
|
const char *script, ...)
|
||||||
{
|
{
|
||||||
@ -2765,7 +2814,7 @@ int lxc_setup(const char *name, struct lxc_conf *lxc_conf)
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (run_lxc_hooks(name, "pre-mount", lxc_conf)) {
|
if (run_lxc_hooks(name, "pre-mount", lxc_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;
|
||||||
}
|
}
|
||||||
@ -2792,13 +2841,13 @@ int lxc_setup(const char *name, struct lxc_conf *lxc_conf)
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (run_lxc_hooks(name, "mount", lxc_conf)) {
|
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) {
|
if (lxc_conf->autodev) {
|
||||||
if (run_lxc_hooks(name, "autodev", lxc_conf)) {
|
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;
|
||||||
}
|
}
|
||||||
@ -2865,7 +2914,7 @@ int lxc_setup(const char *name, struct lxc_conf *lxc_conf)
|
|||||||
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 *hook, struct lxc_conf *conf, char *argv[])
|
||||||
{
|
{
|
||||||
int which = -1;
|
int which = -1;
|
||||||
struct lxc_list *it;
|
struct lxc_list *it;
|
||||||
@ -2882,12 +2931,14 @@ int run_lxc_hooks(const char *name, char *hook, struct lxc_conf *conf)
|
|||||||
which = LXCHOOK_START;
|
which = LXCHOOK_START;
|
||||||
else if (strcmp(hook, "post-stop") == 0)
|
else if (strcmp(hook, "post-stop") == 0)
|
||||||
which = LXCHOOK_POSTSTOP;
|
which = LXCHOOK_POSTSTOP;
|
||||||
|
else if (strcmp(hook, "clone") == 0)
|
||||||
|
which = LXCHOOK_CLONE;
|
||||||
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 *hookname = it->elem;
|
||||||
ret = run_script(name, "lxc", hookname, hook, NULL);
|
ret = run_script_argv(name, "lxc", hookname, hook, argv);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -237,7 +237,7 @@ struct lxc_rootfs {
|
|||||||
*/
|
*/
|
||||||
enum lxchooks {
|
enum lxchooks {
|
||||||
LXCHOOK_PRESTART, LXCHOOK_PREMOUNT, LXCHOOK_MOUNT, LXCHOOK_AUTODEV,
|
LXCHOOK_PRESTART, LXCHOOK_PREMOUNT, LXCHOOK_MOUNT, LXCHOOK_AUTODEV,
|
||||||
LXCHOOK_START, LXCHOOK_POSTSTOP, NUM_LXC_HOOKS};
|
LXCHOOK_START, LXCHOOK_POSTSTOP, LXCHOOK_CLONE, NUM_LXC_HOOKS};
|
||||||
extern char *lxchook_names[NUM_LXC_HOOKS];
|
extern char *lxchook_names[NUM_LXC_HOOKS];
|
||||||
|
|
||||||
struct saved_nic {
|
struct saved_nic {
|
||||||
@ -284,7 +284,7 @@ struct lxc_conf {
|
|||||||
char *rcfile; // Copy of the top level rcfile we read
|
char *rcfile; // Copy of the top level rcfile we read
|
||||||
};
|
};
|
||||||
|
|
||||||
int run_lxc_hooks(const char *name, char *hook, struct lxc_conf *conf);
|
int run_lxc_hooks(const char *name, char *hook, struct lxc_conf *conf, char *argv[]);
|
||||||
|
|
||||||
extern int setup_cgroup(const char *cgpath, struct lxc_list *cgroups);
|
extern int setup_cgroup(const char *cgpath, struct lxc_list *cgroups);
|
||||||
extern int setup_cgroup_devices(const char *cgpath, struct lxc_list *cgroups);
|
extern int setup_cgroup_devices(const char *cgpath, struct lxc_list *cgroups);
|
||||||
|
@ -117,6 +117,7 @@ static struct lxc_config_t config[] = {
|
|||||||
{ "lxc.hook.autodev", config_hook },
|
{ "lxc.hook.autodev", config_hook },
|
||||||
{ "lxc.hook.start", config_hook },
|
{ "lxc.hook.start", config_hook },
|
||||||
{ "lxc.hook.post-stop", config_hook },
|
{ "lxc.hook.post-stop", config_hook },
|
||||||
|
{ "lxc.hook.clone", config_hook },
|
||||||
{ "lxc.network.type", config_network_type },
|
{ "lxc.network.type", config_network_type },
|
||||||
{ "lxc.network.flags", config_network_flags },
|
{ "lxc.network.flags", config_network_flags },
|
||||||
{ "lxc.network.link", config_network_link },
|
{ "lxc.network.link", config_network_link },
|
||||||
@ -894,6 +895,8 @@ static int config_hook(const char *key, const char *value,
|
|||||||
return add_hook(lxc_conf, LXCHOOK_START, copy);
|
return add_hook(lxc_conf, LXCHOOK_START, copy);
|
||||||
else if (strcmp(key, "lxc.hook.post-stop") == 0)
|
else if (strcmp(key, "lxc.hook.post-stop") == 0)
|
||||||
return add_hook(lxc_conf, LXCHOOK_POSTSTOP, copy);
|
return add_hook(lxc_conf, LXCHOOK_POSTSTOP, copy);
|
||||||
|
else if (strcmp(key, "lxc.hook.clone") == 0)
|
||||||
|
return add_hook(lxc_conf, LXCHOOK_CLONE, copy);
|
||||||
SYSERROR("Unknown key: %s", key);
|
SYSERROR("Unknown key: %s", key);
|
||||||
free(copy);
|
free(copy);
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
lxc_log_define(lxc_clone, lxc);
|
lxc_log_define(lxc_clone, lxc);
|
||||||
|
|
||||||
void usage(char *me)
|
void usage(const char *me)
|
||||||
{
|
{
|
||||||
printf("Usage: %s [-s] [-B backingstore] [-L size] [-K] [-M] [-H]\n", me);
|
printf("Usage: %s [-s] [-B backingstore] [-L size] [-K] [-M] [-H]\n", me);
|
||||||
printf(" [-p lxcpath] [-P newlxcpath] orig new\n");
|
printf(" [-p lxcpath] [-P newlxcpath] orig new\n");
|
||||||
@ -60,6 +60,7 @@ int main(int argc, char *argv[])
|
|||||||
long newsize = 0;
|
long newsize = 0;
|
||||||
char *bdevtype = NULL, *lxcpath = NULL, *newpath = NULL, *fstype = NULL;
|
char *bdevtype = NULL, *lxcpath = NULL, *newpath = NULL, *fstype = NULL;
|
||||||
char *orig = NULL, *new = NULL, *vgname = NULL;
|
char *orig = NULL, *new = NULL, *vgname = NULL;
|
||||||
|
char **args = NULL;
|
||||||
char c;
|
char c;
|
||||||
|
|
||||||
if (argc < 3)
|
if (argc < 3)
|
||||||
@ -86,14 +87,13 @@ int main(int argc, char *argv[])
|
|||||||
default: break;
|
default: break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (optind == argc-2 && !orig)
|
if (optind < argc && !orig)
|
||||||
orig = argv[optind++];
|
orig = argv[optind++];
|
||||||
if (optind == argc-1 && !new)
|
if (optind < argc && !new)
|
||||||
new = argv[optind++];
|
new = argv[optind++];
|
||||||
if (optind < argc) {
|
if (optind < argc)
|
||||||
printf("%d extraneous arguments\n", argc-optind);
|
/* arguments for the clone hook */
|
||||||
usage(argv[0]);
|
args = &argv[optind];
|
||||||
}
|
|
||||||
if (!new || !orig) {
|
if (!new || !orig) {
|
||||||
printf("Error: you must provide orig and new names\n");
|
printf("Error: you must provide orig and new names\n");
|
||||||
usage(argv[0]);
|
usage(argv[0]);
|
||||||
@ -124,7 +124,7 @@ int main(int argc, char *argv[])
|
|||||||
lxc_container_put(c1);
|
lxc_container_put(c1);
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
c2 = c1->clone(c1, new, newpath, flags, bdevtype, NULL, newsize);
|
c2 = c1->clone(c1, new, newpath, flags, bdevtype, NULL, newsize, args);
|
||||||
if (c2 == NULL) {
|
if (c2 == NULL) {
|
||||||
lxc_container_put(c1);
|
lxc_container_put(c1);
|
||||||
fprintf(stderr, "clone failed\n");
|
fprintf(stderr, "clone failed\n");
|
||||||
|
@ -1364,16 +1364,14 @@ static int copy_storage(struct lxc_container *c0, struct lxc_container *c,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int clone_update_rootfs(struct lxc_container *c, int flags)
|
static int clone_update_rootfs(struct lxc_container *c, int flags, char **hookargs)
|
||||||
{
|
{
|
||||||
int ret = -1;
|
int ret = -1;
|
||||||
char path[MAXPATHLEN];
|
char path[MAXPATHLEN];
|
||||||
struct bdev *bdev;
|
struct bdev *bdev;
|
||||||
FILE *fout;
|
FILE *fout;
|
||||||
pid_t pid;
|
pid_t pid;
|
||||||
|
struct lxc_conf *conf = c->lxc_conf;
|
||||||
if (flags & LXC_CLONE_KEEPNAME)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
/* update hostname in rootfs */
|
/* update hostname in rootfs */
|
||||||
/* we're going to mount, so run in a clean namespace to simplify cleanup */
|
/* we're going to mount, so run in a clean namespace to simplify cleanup */
|
||||||
@ -1393,6 +1391,29 @@ static int clone_update_rootfs(struct lxc_container *c, int flags)
|
|||||||
exit(1);
|
exit(1);
|
||||||
if (bdev->ops->mount(bdev) < 0)
|
if (bdev->ops->mount(bdev) < 0)
|
||||||
exit(1);
|
exit(1);
|
||||||
|
|
||||||
|
if (!lxc_list_empty(&conf->hooks[LXCHOOK_CLONE])) {
|
||||||
|
/* Start of environment variable setup for hooks */
|
||||||
|
if (setenv("LXC_NAME", c->name, 1)) {
|
||||||
|
SYSERROR("failed to set environment variable for container name");
|
||||||
|
}
|
||||||
|
if (setenv("LXC_CONFIG_FILE", conf->rcfile, 1)) {
|
||||||
|
SYSERROR("failed to set environment variable for config path");
|
||||||
|
}
|
||||||
|
if (setenv("LXC_ROOTFS_MOUNT", conf->rootfs.mount, 1)) {
|
||||||
|
SYSERROR("failed to set environment variable for rootfs mount");
|
||||||
|
}
|
||||||
|
if (setenv("LXC_ROOTFS_PATH", conf->rootfs.path, 1)) {
|
||||||
|
SYSERROR("failed to set environment variable for rootfs mount");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (run_lxc_hooks(c->name, "clone", conf, hookargs)) {
|
||||||
|
ERROR("Error executing clone hook for %s", c->name);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(flags & LXC_CLONE_KEEPNAME)) {
|
||||||
ret = snprintf(path, MAXPATHLEN, "%s/etc/hostname", bdev->dest);
|
ret = snprintf(path, MAXPATHLEN, "%s/etc/hostname", bdev->dest);
|
||||||
if (ret < 0 || ret >= MAXPATHLEN)
|
if (ret < 0 || ret >= MAXPATHLEN)
|
||||||
exit(1);
|
exit(1);
|
||||||
@ -1404,6 +1425,7 @@ static int clone_update_rootfs(struct lxc_container *c, int flags)
|
|||||||
exit(1);
|
exit(1);
|
||||||
if (fclose(fout) < 0)
|
if (fclose(fout) < 0)
|
||||||
exit(1);
|
exit(1);
|
||||||
|
}
|
||||||
exit(0);
|
exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1436,7 +1458,8 @@ static int create_file_dirname(char *path)
|
|||||||
|
|
||||||
struct lxc_container *lxcapi_clone(struct lxc_container *c, const char *newname,
|
struct lxc_container *lxcapi_clone(struct lxc_container *c, const char *newname,
|
||||||
const char *lxcpath, int flags,
|
const char *lxcpath, int flags,
|
||||||
const char *bdevtype, const char *bdevdata, unsigned long newsize)
|
const char *bdevtype, const char *bdevdata, unsigned long newsize,
|
||||||
|
char **hookargs)
|
||||||
{
|
{
|
||||||
struct lxc_container *c2 = NULL;
|
struct lxc_container *c2 = NULL;
|
||||||
char newpath[MAXPATHLEN];
|
char newpath[MAXPATHLEN];
|
||||||
@ -1525,7 +1548,7 @@ struct lxc_container *lxcapi_clone(struct lxc_container *c, const char *newname,
|
|||||||
if (!c2->save_config(c2, NULL))
|
if (!c2->save_config(c2, NULL))
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
if (clone_update_rootfs(c2, flags) < 0)
|
if (clone_update_rootfs(c2, flags, hookargs) < 0)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
// TODO: update c's lxc.snapshot = count
|
// TODO: update c's lxc.snapshot = count
|
||||||
|
@ -105,7 +105,7 @@ struct lxc_container {
|
|||||||
*/
|
*/
|
||||||
struct lxc_container *(*clone)(struct lxc_container *c, const char *newname,
|
struct lxc_container *(*clone)(struct lxc_container *c, const char *newname,
|
||||||
const char *lxcpath, int flags, const char *bdevtype,
|
const char *lxcpath, int flags, const char *bdevtype,
|
||||||
const char *bdevdata, unsigned long newsize);
|
const char *bdevdata, unsigned long newsize, char **hookargs);
|
||||||
|
|
||||||
#if 0
|
#if 0
|
||||||
bool (*commit_cgroups)(struct lxc_container *c);
|
bool (*commit_cgroups)(struct lxc_container *c);
|
||||||
|
@ -461,7 +461,7 @@ struct lxc_handler *lxc_init(const char *name, struct lxc_conf *conf, const char
|
|||||||
}
|
}
|
||||||
/* End of environment variable setup for hooks */
|
/* End of environment variable setup for hooks */
|
||||||
|
|
||||||
if (run_lxc_hooks(name, "pre-start", conf)) {
|
if (run_lxc_hooks(name, "pre-start", conf, NULL)) {
|
||||||
ERROR("failed to run pre-start hooks for container '%s'.", name);
|
ERROR("failed to run pre-start hooks for container '%s'.", name);
|
||||||
goto out_aborting;
|
goto out_aborting;
|
||||||
}
|
}
|
||||||
@ -513,7 +513,7 @@ static void lxc_fini(const char *name, struct lxc_handler *handler)
|
|||||||
lxc_set_state(name, handler, STOPPING);
|
lxc_set_state(name, handler, STOPPING);
|
||||||
lxc_set_state(name, handler, STOPPED);
|
lxc_set_state(name, handler, STOPPED);
|
||||||
|
|
||||||
if (run_lxc_hooks(name, "post-stop", handler->conf))
|
if (run_lxc_hooks(name, "post-stop", handler->conf, NULL))
|
||||||
ERROR("failed to run post-stop hooks for container '%s'.", name);
|
ERROR("failed to run post-stop hooks for container '%s'.", name);
|
||||||
|
|
||||||
/* reset mask set by setup_signal_fd */
|
/* reset mask set by setup_signal_fd */
|
||||||
@ -676,7 +676,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)) {
|
if (run_lxc_hooks(handler->name, "start", handler->conf, NULL)) {
|
||||||
ERROR("failed to run start hooks for container '%s'.", handler->name);
|
ERROR("failed to run start hooks for container '%s'.", handler->name);
|
||||||
goto out_warn_father;
|
goto out_warn_father;
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user