]> git.proxmox.com Git - mirror_lxc.git/commitdiff
Introduce a first set of container hooks
authorSerge Hallyn <serge.hallyn@ubuntu.com>
Tue, 31 Jul 2012 14:04:33 +0000 (16:04 +0200)
committerDaniel Lezcano <daniel.lezcano@free.fr>
Tue, 31 Jul 2012 14:04:33 +0000 (16:04 +0200)
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.

Signed-off-by: Serge Hallyn <serge.hallyn@ubuntu.com>
Signed-off-by: Daniel Lezcano <dlezcano@fr.ibm.com>
src/lxc/conf.c
src/lxc/conf.h
src/lxc/confile.c
src/lxc/start.c

index 724a26c742aa74ac93029a2d85ad52dcceff69f1..4b79d50b59abd1f35877e87de59c6c2459073b39 100644 (file)
@@ -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; i<NUM_LXC_HOOKS; i++)
+               lxc_list_init(&new->hooks[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;
+}
index 0c1f5c31733517def90ddc7c56e768077c3a82a7..7c1ded00f370a349382f7aedb639563b7e190010 100644 (file)
@@ -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
  */
index 09902ba4b8e01ebd6ff5fc21f82eebc5b52f6d60..b7b990ee0b25ca37551f72a4d83243f6f0f40db5 100644 (file)
@@ -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)
 {
index fffa4cd2cbdb285cdc00ac14fb5e3fe26b5ff878..48e9962e7d4897d6517e2421614b4455046eb90d 100644 (file)
@@ -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))