From: Christian Brauner Date: Wed, 11 Oct 2017 09:13:53 +0000 (+0200) Subject: lxccontainer: implement container live patching X-Git-Tag: lxc-3.0.0.beta1~218^2~1 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=0d9cd9c37a7fa1dabec171a2b2f95d21ad6d0730;p=thirdparty%2Flxc.git lxccontainer: implement container live patching 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 --- diff --git a/src/lxc/commands.c b/src/lxc/commands.c index 68fbd387c..4d440ba78 100644 --- a/src/lxc/commands.c +++ b/src/lxc/commands.c @@ -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) { diff --git a/src/lxc/commands.h b/src/lxc/commands.h index 28428c774..cc5eec3c5 100644 --- a/src/lxc/commands.h +++ b/src/lxc/commands.h @@ -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 */ diff --git a/src/lxc/lxc.h b/src/lxc/lxc.h index c3c55828a..c9064ff08 100644 --- a/src/lxc/lxc.h +++ b/src/lxc/lxc.h @@ -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 diff --git a/src/lxc/lxccontainer.c b/src/lxc/lxccontainer.c index 5e8ad00f9..2df8c183e 100644 --- a/src/lxc/lxccontainer.c +++ b/src/lxc/lxccontainer.c @@ -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; diff --git a/src/lxc/lxccontainer.h b/src/lxc/lxccontainer.h index 3aee440e4..85139115d 100644 --- a/src/lxc/lxccontainer.h +++ b/src/lxc/lxccontainer.h @@ -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. *