diff --git a/src/lxc/conf.c b/src/lxc/conf.c index 724a26c74..4b79d50b5 100644 --- a/src/lxc/conf.c +++ b/src/lxc/conf.c @@ -1617,6 +1617,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) { @@ -1636,6 +1637,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; ihooks[i]); #if HAVE_APPARMOR new->aa_profile = NULL; #endif @@ -2067,6 +2070,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; @@ -2116,3 +2120,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 0c1f5c317..7c1ded00f 100644 --- a/src/lxc/conf.h +++ b/src/lxc/conf.h @@ -202,6 +202,9 @@ struct lxc_rootfs { * @aa_profile : apparmor profile to switch to #endif */ +enum lxchooks { + LXCHOOK_PRESTART, LXCHOOK_MOUNT, LXCHOOK_START, + LXCHOOK_POSTSTOP, NUM_LXC_HOOKS}; struct lxc_conf { char *fstab; int tty; @@ -219,6 +222,7 @@ struct lxc_conf { struct lxc_rootfs rootfs; char *ttydir; int close_all_fds; + struct lxc_list hooks[NUM_LXC_HOOKS]; #if HAVE_APPARMOR char *aa_profile; #endif @@ -227,6 +231,14 @@ struct lxc_conf { #endif }; +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 09902ba4b..b7b990ee0 100644 --- a/src/lxc/confile.c +++ b/src/lxc/confile.c @@ -58,6 +58,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 *); @@ -97,6 +98,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 }, @@ -590,6 +595,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 fffa4cd2c..48e9962e7 100644 --- a/src/lxc/start.c +++ b/src/lxc/start.c @@ -359,6 +359,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; @@ -403,6 +405,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"); @@ -524,6 +528,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))