]> git.ipfire.org Git - thirdparty/lxc.git/commitdiff
lxccontainer: implement container live patching
authorChristian Brauner <christian.brauner@ubuntu.com>
Wed, 11 Oct 2017 09:13:53 +0000 (11:13 +0200)
committerChristian Brauner <christian.brauner@ubuntu.com>
Wed, 11 Oct 2017 12:02:46 +0000 (14:02 +0200)
This adds set_running_config_item() which is the analogue of
get_running_config_item(). In essence it allows a caller to livepatch the
container's in-memory configuration. This POC is severly limited. Here are the
most obvious ones:
- Only the container's in-memory config can be updated but no further actions
  (e.g. on-disk actions) are made.
- Only keys in the "lxc.net." namespace can be changed. This POC also allows
  updating an existing network. For example it allows to change the network
  type of an existing network. This is obviously nonsense and in a non-POC
  implementation this should be blocked.

Use Case:
Callers can hotplug a new network for the container. For example, LXD can
create a pair of veth devices in the host and in the container and add it to
the container's in-memory config. This means, the container can later be
queried for the name of the device later on etc. Note that liblxc will
currently not delete hotplugged network devices on container shutdown since it
won't have the ifindex of the container.

Relates to https://github.com/lxc/lxd/issues/3920 .

Signed-off-by: Christian Brauner <christian.brauner@ubuntu.com>
src/lxc/commands.c
src/lxc/commands.h
src/lxc/lxc.h
src/lxc/lxccontainer.c
src/lxc/lxccontainer.h

index 68fbd387cb3edd431b385ea31fc20d009c24002d..4d440ba78261b833caedac1743d247b3ba4f9cd6 100644 (file)
@@ -90,6 +90,7 @@ static const char *lxc_cmd_str(lxc_cmd_t cmd)
                [LXC_CMD_GET_NAME]         = "get_name",
                [LXC_CMD_GET_LXCPATH]      = "get_lxcpath",
                [LXC_CMD_ADD_STATE_CLIENT] = "add_state_client",
+               [LXC_CMD_SET_CONFIG_ITEM]  = "set_config_item",
        };
 
        if (cmd >= LXC_CMD_MAX)
@@ -538,6 +539,59 @@ out:
        return lxc_cmd_rsp_send(fd, &rsp);
 }
 
+/*
+ * lxc_cmd_set_config_item: Get config item the running container
+ *
+ * @name     : name of container to connect to
+ * @item     : the configuration item to set (ex: lxc.net.0.veth.pair)
+ * @value    : the value to set (ex: "eth0")
+ * @lxcpath  : the lxcpath in which the container is running
+ *
+ * Returns 0 on success, negative errno on failure.
+ */
+int lxc_cmd_set_config_item(const char *name, const char *item,
+                           const char *value, const char *lxcpath)
+{
+       int ret, stopped;
+       struct lxc_cmd_set_config_item_req_data data;
+       struct lxc_cmd_rr cmd;
+
+       /* pre-validate request
+          Currently we only support live-patching network configurations.
+        */
+       if (strncmp(item, "lxc.net.", 8))
+               return -EINVAL;
+
+       data.item = item;
+       data.value = (void *)value;
+
+       cmd.req.cmd = LXC_CMD_SET_CONFIG_ITEM;
+       cmd.req.data = &data;
+       cmd.req.datalen = sizeof(data);
+
+       ret = lxc_cmd(name, &cmd, &stopped, lxcpath, NULL);
+       if (ret < 0)
+               return ret;
+
+       return cmd.rsp.ret;
+}
+
+static int lxc_cmd_set_config_item_callback(int fd, struct lxc_cmd_req *req,
+                                           struct lxc_handler *handler)
+{
+       const char *key, *value;
+       struct lxc_cmd_rsp rsp;
+       const struct lxc_cmd_set_config_item_req_data *data;
+
+       data = req->data;
+       key = data->item;
+       value = data->value;
+
+       memset(&rsp, 0, sizeof(rsp));
+       rsp.ret = lxc_set_config_item_locked(handler->conf, key, value);
+       return lxc_cmd_rsp_send(fd, &rsp);
+}
+
 /*
  * lxc_cmd_get_state: Get current state of the container
  *
@@ -949,6 +1003,7 @@ static int lxc_cmd_process(int fd, struct lxc_cmd_req *req,
                [LXC_CMD_GET_NAME]         = lxc_cmd_get_name_callback,
                [LXC_CMD_GET_LXCPATH]      = lxc_cmd_get_lxcpath_callback,
                [LXC_CMD_ADD_STATE_CLIENT] = lxc_cmd_add_state_client_callback,
+               [LXC_CMD_SET_CONFIG_ITEM]  = lxc_cmd_set_config_item_callback,
        };
 
        if (req->cmd >= LXC_CMD_MAX) {
index 28428c774e788f024dfce0c3c2f659ad8eff599c..cc5eec3c55267b253ed914071740e7de4e4284f9 100644 (file)
@@ -48,6 +48,7 @@ typedef enum {
        LXC_CMD_GET_NAME,
        LXC_CMD_GET_LXCPATH,
        LXC_CMD_ADD_STATE_CLIENT,
+       LXC_CMD_SET_CONFIG_ITEM,
        LXC_CMD_MAX,
 } lxc_cmd_t;
 
@@ -73,6 +74,11 @@ struct lxc_cmd_console_rsp_data {
        int ttynum;
 };
 
+struct lxc_cmd_set_config_item_req_data {
+       const char *item;
+       void *value;
+};
+
 extern int lxc_cmd_console_winch(const char *name, const char *lxcpath);
 extern int lxc_cmd_console(const char *name, int *ttynum, int *fd,
                           const char *lxcpath);
@@ -116,4 +122,7 @@ extern int lxc_cmd_mainloop_add(const char *name, struct lxc_epoll_descr *descr,
                                    struct lxc_handler *handler);
 extern int lxc_try_cmd(const char *name, const char *lxcpath);
 
+extern int lxc_cmd_set_config_item(const char *name, const char *item,
+                                  const char *value, const char *lxcpath);
+
 #endif /* __commands_h */
index c3c55828a8d7b54ff9f8d34a1498d8b42a75fef1..c9064ff08c75e1ed1ee81126d17613f47693a0bf 100644 (file)
@@ -149,6 +149,13 @@ extern int lxc_get_wait_states(const char **states);
  */
 extern int add_rdepend(struct lxc_conf *lxc_conf, char *rdepend);
 
+/*
+ * Set a key/value configuration option. Requires that to take a lock on the
+ * in-memory config of the container.
+ */
+extern int lxc_set_config_item_locked(struct lxc_conf *conf, const char *key,
+                                     const char *v);
+
 #ifdef __cplusplus
 }
 #endif
index 5e8ad00f98ae9d55c858883b0504be59b630dbe3..2df8c183e8a2f709c7b93572b0fc5a00a315ec49 100644 (file)
@@ -2765,9 +2765,35 @@ static bool do_lxcapi_destroy_with_snapshots(struct lxc_container *c)
 
 WRAP_API(bool, lxcapi_destroy_with_snapshots)
 
-static bool set_config_item_locked(struct lxc_container *c, const char *key, const char *v)
+int lxc_set_config_item_locked(struct lxc_conf *conf, const char *key,
+                              const char *v)
 {
+       int ret;
        struct lxc_config_t *config;
+       bool bret = true;
+
+       config = lxc_get_config(key);
+       if (!config)
+               return -EINVAL;
+
+       ret = config->set(key, v, conf, NULL);
+       if (ret < 0)
+               return -EINVAL;
+
+       if (lxc_config_value_empty(v))
+               do_clear_unexp_config_line(conf, key);
+       else
+               bret = do_append_unexp_config_line(conf, key, v);
+       if (!bret)
+               return -ENOMEM;
+
+       return 0;
+}
+
+static bool do_set_config_item_locked(struct lxc_container *c, const char *key,
+                                     const char *v)
+{
+       int ret;
 
        if (!c->lxc_conf)
                c->lxc_conf = lxc_conf_init();
@@ -2775,19 +2801,11 @@ static bool set_config_item_locked(struct lxc_container *c, const char *key, con
        if (!c->lxc_conf)
                return false;
 
-       config = lxc_get_config(key);
-       if (!config)
-               return false;
-
-       if (config->set(key, v, c->lxc_conf, NULL) != 0)
+       ret = lxc_set_config_item_locked(c->lxc_conf, key, v);
+       if (ret < 0)
                return false;
 
-       if (lxc_config_value_empty(v)) {
-               do_clear_unexp_config_line(c->lxc_conf, key);
-               return true;
-       }
-
-       return do_append_unexp_config_line(c->lxc_conf, key, v);
+       return true;
 }
 
 static bool do_lxcapi_set_config_item(struct lxc_container *c, const char *key, const char *v)
@@ -2800,7 +2818,7 @@ static bool do_lxcapi_set_config_item(struct lxc_container *c, const char *key,
        if (container_mem_lock(c))
                return false;
 
-       b = set_config_item_locked(c, key, v);
+       b = do_set_config_item_locked(c, key, v);
 
        container_mem_unlock(c);
        return b;
@@ -2808,6 +2826,26 @@ static bool do_lxcapi_set_config_item(struct lxc_container *c, const char *key,
 
 WRAP_API_2(bool, lxcapi_set_config_item, const char *, const char *)
 
+static bool do_lxcapi_set_running_config_item(struct lxc_container *c, const char *key, const char *v)
+{
+       int ret;
+
+       if (!c)
+               return false;
+
+       if (container_mem_lock(c))
+               return false;
+
+       ret = lxc_cmd_set_config_item(c->name, key, v, do_lxcapi_get_config_path(c));
+       if (ret < 0)
+               ERROR("Failed to live patch container");
+
+       container_mem_unlock(c);
+       return ret == 0;
+}
+
+WRAP_API_2(bool, lxcapi_set_running_config_item, const char *, const char *)
+
 static char *lxcapi_config_file_name(struct lxc_container *c)
 {
        if (!c || !c->configfile)
@@ -3502,7 +3540,7 @@ static struct lxc_container *do_lxcapi_clone(struct lxc_container *c, const char
                clear_unexp_config_line(c2->lxc_conf, "lxc.utsname", false);
                clear_unexp_config_line(c2->lxc_conf, "lxc.uts.name", false);
 
-               if (!set_config_item_locked(c2, "lxc.uts.name", newname)) {
+               if (!do_set_config_item_locked(c2, "lxc.uts.name", newname)) {
                        ERROR("Error setting new hostname");
                        goto out;
                }
@@ -4544,6 +4582,7 @@ struct lxc_container *lxc_container_new(const char *name, const char *configpath
        c->clear_config_item = lxcapi_clear_config_item;
        c->get_config_item = lxcapi_get_config_item;
        c->get_running_config_item = lxcapi_get_running_config_item;
+       c->set_running_config_item = lxcapi_set_running_config_item;
        c->get_cgroup_item = lxcapi_get_cgroup_item;
        c->set_cgroup_item = lxcapi_set_cgroup_item;
        c->get_config_path = lxcapi_get_config_path;
index 3aee440e4ec788d0e22da40144c44eae4758d2a6..85139115d001e4f47550a397d00600e8f80f2504 100644 (file)
@@ -279,6 +279,17 @@ struct lxc_container {
         */
        bool (*set_config_item)(struct lxc_container *c, const char *key, const char *value);
 
+       /*!
+        * \brief Set a key/value configuration option on a running container.
+        *
+        * \param c Container.
+        * \param key Name of option to set.
+        * \param value Value of \p name to set.
+        *
+        * \return \c true on success, else \c false.
+        */
+       bool (*set_running_config_item)(struct lxc_container *c, const char *key, const char *value);
+
        /*!
         * \brief Delete the container.
         *