This patch introduces support for 4 hooks. We'd like to have 6 in all to mirror the openvz ones (thanks to Stéphane for this info):
pre-start: in the host namespace before container mounting happens mount: after container mounting (as per config and /var/lib/lxc/container/fstab) but before pivot_root start: immediately before exec'ing init stop: in container namespace and in chroot before shutdown umount: after other unmounting has happened post-stop: outside of the container stop and umount are not implemented here because when the kernel kills the container init, it kills the namespace. We can probably work around this, i.e. by keeping the /proc/pid/ns/mnt open, and using that, though all container tasks including init would still be dead. Is that worth pursuing? start also presents a bit of an issue. openvz allows a script on the host to be specified, apparently. My patch requires the script or program to exist in the container. I'm fine with trying to do it the openvz way, but I wasn't sure what the best way to do that was. Openvz (I'm told) opens the script and passes its contents to a bash in the container. But that limits the hooks to being only scripts. By requiring the hook to be in the container, we can allow any sort of hook, and assume that any required libraries/dependencies exist there. Other than that with this patchset I can add lxc.hook.pre-start = /var/lib/lxc/p1/pre-start lxc.hook.mount = /var/lib/lxc/p1/mount lxc.hook.start = /start lxc.hook.post-stop = /var/lib/lxc/p1/post-stop to my /var/lib/lxc/p1/config, and the hooks get executed as expected. Comments on the patch, the hooks we want, and the questions raised above would be appreciated. Signed-off-by: Serge Hallyn <serge.hal...@ubuntu.com> --- src/lxc/conf.c | 29 +++++++++++++++++++++++++++++ src/lxc/conf.h | 12 ++++++++++++ src/lxc/confile.c | 40 ++++++++++++++++++++++++++++++++++++++++ src/lxc/start.c | 6 ++++++ 4 files changed, 87 insertions(+) diff --git a/src/lxc/conf.c b/src/lxc/conf.c index e8088bb..546d9fc 100644 --- a/src/lxc/conf.c +++ b/src/lxc/conf.c @@ -1572,6 +1572,7 @@ static int setup_private_host_hw_addr(char *veth1) struct lxc_conf *lxc_conf_init(void) { struct lxc_conf *new; + int i; new = malloc(sizeof(*new)); if (!new) { @@ -1591,6 +1592,8 @@ struct lxc_conf *lxc_conf_init(void) lxc_list_init(&new->network); lxc_list_init(&new->mount_list); lxc_list_init(&new->caps); + for (i=0; i<NUM_LXC_HOOKS; i++) + lxc_list_init(&new->hooks[i]); return new; } @@ -2012,6 +2015,7 @@ int lxc_setup(const char *name, struct lxc_conf *lxc_conf) return -1; } + HOOK(name, "mount", lxc_conf); if (setup_cgroup(name, &lxc_conf->cgroup)) { ERROR("failed to setup the cgroups for '%s'", name); return -1; @@ -2051,3 +2055,28 @@ int lxc_setup(const char *name, struct lxc_conf *lxc_conf) return 0; } + +int run_lxc_hooks(const char *name, char *hook, struct lxc_conf *conf) +{ + int which = -1; + struct lxc_list *it; + + if (strcmp(hook, "pre-start") == 0) + which = LXCHOOK_PRESTART; + else if (strcmp(hook, "mount") == 0) + which = LXCHOOK_MOUNT; + else if (strcmp(hook, "start") == 0) + which = LXCHOOK_START; + else if (strcmp(hook, "post-stop") == 0) + which = LXCHOOK_POSTSTOP; + else + return -1; + lxc_list_for_each(it, &conf->hooks[which]) { + int ret; + char *hookname = it->elem; + ret = run_script(name, "lxc", hookname, hook, NULL); + if (ret) + return ret; + } + return 0; +} diff --git a/src/lxc/conf.h b/src/lxc/conf.h index 09f55cb..c91872b 100644 --- a/src/lxc/conf.h +++ b/src/lxc/conf.h @@ -199,6 +199,9 @@ struct lxc_rootfs { * @console : console data * @ttydir : directory (under /dev) in which to create console and ttys */ +enum lxchooks { + LXCHOOK_PRESTART, LXCHOOK_MOUNT, LXCHOOK_START, + LXCHOOK_POSTSTOP, NUM_LXC_HOOKS}; struct lxc_conf { char *fstab; int tty; @@ -216,8 +219,17 @@ struct lxc_conf { struct lxc_rootfs rootfs; char *ttydir; int close_all_fds; + struct lxc_list hooks[NUM_LXC_HOOKS]; }; +int run_lxc_hooks(const char *name, char *hook, struct lxc_conf *conf); +/* we don't want to stick with the HOOK define, it's just to easily start */ +#define HOOK(name, which, conf) \ + do { \ + int hookret = run_lxc_hooks(name, which, conf); \ + if (hookret) return -1; \ + } while (0); + /* * Initialize the lxc configuration structure */ diff --git a/src/lxc/confile.c b/src/lxc/confile.c index b305aef..75cf4d7 100644 --- a/src/lxc/confile.c +++ b/src/lxc/confile.c @@ -55,6 +55,7 @@ static int config_rootfs(const char *, char *, struct lxc_conf *); static int config_rootfs_mount(const char *, char *, struct lxc_conf *); static int config_pivotdir(const char *, char *, struct lxc_conf *); static int config_utsname(const char *, char *, struct lxc_conf *); +static int config_hook(const char *key, char *value, struct lxc_conf *lxc_conf); static int config_network_type(const char *, char *, struct lxc_conf *); static int config_network_flags(const char *, char *, struct lxc_conf *); static int config_network_link(const char *, char *, struct lxc_conf *); @@ -91,6 +92,10 @@ static struct config config[] = { { "lxc.rootfs", config_rootfs }, { "lxc.pivotdir", config_pivotdir }, { "lxc.utsname", config_utsname }, + { "lxc.hook.pre-start", config_hook }, + { "lxc.hook.mount", config_hook }, + { "lxc.hook.start", config_hook }, + { "lxc.hook.post-stop", config_hook }, { "lxc.network.type", config_network_type }, { "lxc.network.flags", config_network_flags }, { "lxc.network.link", config_network_link }, @@ -584,6 +589,41 @@ static int config_network_script(const char *key, char *value, return -1; } +static int add_hook(struct lxc_conf *lxc_conf, int which, char *hook) +{ + struct lxc_list *hooklist; + + hooklist = malloc(sizeof(*hooklist)); + if (!hooklist) { + free(hook); + return -1; + } + hooklist->elem = hook; + lxc_list_add_tail(&lxc_conf->hooks[which], hooklist); + return 0; +} + +static int config_hook(const char *key, char *value, + struct lxc_conf *lxc_conf) +{ + char *copy = strdup(value); + if (!copy) { + SYSERROR("failed to dup string '%s'", value); + return -1; + } + if (strcmp(key, "lxc.hook.pre-start") == 0) + return add_hook(lxc_conf, LXCHOOK_PRESTART, copy); + else if (strcmp(key, "lxc.hook.mount") == 0) + return add_hook(lxc_conf, LXCHOOK_MOUNT, copy); + else if (strcmp(key, "lxc.hook.start") == 0) + return add_hook(lxc_conf, LXCHOOK_START, copy); + else if (strcmp(key, "lxc.hook.post-stop") == 0) + return add_hook(lxc_conf, LXCHOOK_POSTSTOP, copy); + SYSERROR("Unknown key: %s", key); + free(copy); + return -1; +} + static int config_personality(const char *key, char *value, struct lxc_conf *lxc_conf) { diff --git a/src/lxc/start.c b/src/lxc/start.c index 920ff77..eba444d 100644 --- a/src/lxc/start.c +++ b/src/lxc/start.c @@ -357,6 +357,8 @@ struct lxc_handler *lxc_init(const char *name, struct lxc_conf *conf) goto out_free_name; } + HOOK(name, "pre-start", conf); + if (lxc_create_tty(name, conf)) { ERROR("failed to create the ttys"); goto out_aborting; @@ -401,6 +403,8 @@ void lxc_fini(const char *name, struct lxc_handler *handler) lxc_set_state(name, handler, STOPPING); lxc_set_state(name, handler, STOPPED); + HOOK(name, "post-stop", handler->conf); + /* reset mask set by setup_signal_fd */ if (sigprocmask(SIG_SETMASK, &handler->oldmask, NULL)) WARN("failed to restore sigprocmask"); @@ -519,6 +523,8 @@ static int do_start(void *data) close(handler->sigfd); + HOOK(handler->name, "start", handler->conf); + /* after this call, we are in error because this * ops should not return as it execs */ if (handler->ops->start(handler, handler->data)) -- 1.7.9.5 ------------------------------------------------------------------------------ Live Security Virtual Conference Exclusive live event will cover all the ways today's security and threat landscape has changed and how IT managers can respond. Discussions will include endpoint security, mobile security and the latest in malware threats. http://www.accelacomm.com/jaw/sfrnl04242012/114/50122263/ _______________________________________________ Lxc-devel mailing list Lxc-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/lxc-devel