iSulad/src/services/execution/execute/execution_extend.c
2019-11-06 19:33:20 +08:00

1359 lines
37 KiB
C

/******************************************************************************
* Copyright (c) Huawei Technologies Co., Ltd. 2017-2019. All rights reserved.
* iSulad licensed under the Mulan PSL v1.
* You can use this software according to the terms and conditions of the Mulan PSL v1.
* You may obtain a copy of Mulan PSL v1 at:
* http://license.coscl.org.cn/MulanPSL
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
* PURPOSE.
* See the Mulan PSL v1 for more details.
* Author: tanyifeng
* Create: 2017-11-22
* Description: provide container extend callback function definition
*********************************************************************************/
#include <stdio.h>
#include <unistd.h>
#include <sys/time.h>
#include <lcr/lcrcontainer.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <ctype.h>
#include <sys/stat.h>
#include <pthread.h>
#include <sys/sysinfo.h>
#include "log.h"
#include "engine.h"
#include "collector.h"
#include "console.h"
#include "lcrd_config.h"
#include "config.h"
#include "restartmanager.h"
#include "image.h"
#include "securec.h"
#include "verify.h"
#include "container_inspect.h"
#include "containers_store.h"
#include "containers_gc.h"
#include "execution_extend.h"
#include "sysinfo.h"
#include "health_check.h"
#ifdef ENABLE_OCI_IMAGE
#include "oci_rootfs_export.h"
#endif
#include "filters.h"
#include "utils.h"
#include "error.h"
struct stats_context {
struct filters_args *stats_filters;
container_stats_request *stats_config;
};
static int service_events_handler(const struct lcrd_events_request *request,
const stream_func_wrapper *stream)
{
int ret = 0;
char *name = NULL;
container_t *container = NULL;
name = request->id;
/* check whether specified container exists */
if (name != NULL) {
container = containers_store_get(name);
if (container == NULL) {
ERROR("No such container:%s", name);
lcrd_set_error_message("No such container:%s", name);
ret = -1;
goto out;
}
container_unref(container);
}
ret = events_subscribe(name, &request->since, &request->until, stream);
if (ret < 0) {
ERROR("Failed to subscribe events buffer");
ret = -1;
goto out;
}
if (add_monitor_client(name, &request->since, &request->until, stream)) {
ERROR("Failed to add events monitor client");
ret = -1;
goto out;
}
out:
return ret;
}
static int container_events_cb(const struct lcrd_events_request *request, const stream_func_wrapper *stream)
{
int ret = 0;
uint32_t cc = LCRD_SUCCESS;
DAEMON_CLEAR_ERRMSG();
if (request == NULL) {
ERROR("Invalid NULL input");
return -1;
}
if (stream == NULL) {
ERROR("Should provide stream function in events");
cc = LCRD_ERR_INPUT;
goto out;
}
ret = service_events_handler(request, stream);
if (ret != 0) {
ERROR("Failed to add events monitor");
cc = LCRD_ERR_INPUT;
goto out;
}
out:
return (cc == LCRD_SUCCESS) ? 0 : -1;
}
static int dup_container_stats_request(const container_stats_request *src, container_stats_request **dest)
{
int ret = -1;
char *json = NULL;
parser_error err = NULL;
if (src == NULL) {
*dest = NULL;
return 0;
}
json = container_stats_request_generate_json(src, NULL, &err);
if (json == NULL) {
ERROR("Failed to generate json: %s", err);
goto out;
}
*dest = container_stats_request_parse_data(json, NULL, &err);
if (*dest == NULL) {
ERROR("Failed to parse json: %s", err);
goto out;
}
ret = 0;
out:
free(err);
free(json);
return ret;
}
static void free_stats_context(struct stats_context *ctx)
{
if (ctx == NULL) {
return;
}
filters_args_free(ctx->stats_filters);
ctx->stats_filters = NULL;
free_container_stats_request(ctx->stats_config);
ctx->stats_config = NULL;
free(ctx);
}
static struct stats_context *stats_context_new(const container_stats_request *request)
{
struct stats_context *ctx = NULL;
ctx = util_common_calloc_s(sizeof(struct stats_context));
if (ctx == NULL) {
ERROR("Out of memory");
return NULL;
}
ctx->stats_filters = filters_args_new();
if (ctx->stats_filters == NULL) {
ERROR("Out of memory");
goto cleanup;
}
if (dup_container_stats_request(request, &(ctx->stats_config)) != 0) {
ERROR("Failed to dup stats request");
goto cleanup;
}
return ctx;
cleanup:
free_stats_context(ctx);
return NULL;
}
static const char *accepted_stats_filter_tags[] = {
"id",
"label",
"name",
NULL
};
static int copy_map_labels(const container_config *config, map_t **map_labels)
{
*map_labels = map_new(MAP_STR_STR, MAP_DEFAULT_CMP_FUNC, MAP_DEFAULT_FREE_FUNC);
if (*map_labels == NULL) {
ERROR("Out of memory");
return -1;
}
if (config != NULL && config->labels != NULL && config->labels->len != 0) {
size_t i;
json_map_string_string *labels = config->labels;
for (i = 0; i < labels->len; i++) {
// Copy labels to internal map for filters
if (!map_replace(*map_labels, (void *)labels->keys[i], labels->values[i])) {
ERROR("Failed to insert labels to map");
return -1;
}
}
}
return 0;
}
static container_info *get_container_stats(const container_t *cont, const struct engine_container_info *einfo,
const struct stats_context *ctx)
{
int ret = 0;
uint64_t sysmem_limit;
uint64_t sys_cpu_usage = 0;
container_info *info = NULL;
map_t *map_labels = NULL;
info = util_common_calloc_s(sizeof(container_info));
if (info == NULL) {
ERROR("Out of memory");
return NULL;
}
info->id = util_strdup_s(cont->common_config->id);
info->has_pid = einfo->has_pid;
info->pid = (int32_t)einfo->pid;
info->status = (int)einfo->status;
info->pids_current = einfo->pids_current;
info->cpu_use_nanos = einfo->cpu_use_nanos;
info->blkio_read = einfo->blkio_read;
info->blkio_write = einfo->blkio_write;
info->mem_used = einfo->mem_used;
info->mem_limit = einfo->mem_limit;
info->kmem_used = einfo->kmem_used;
info->kmem_limit = einfo->kmem_limit;
sysmem_limit = get_default_total_mem_size();
if (get_system_cpu_usage(&sys_cpu_usage)) {
WARN("Failed to get system cpu usage");
}
if (sysmem_limit > 0) {
if (info->mem_limit > sysmem_limit) {
info->mem_limit = sysmem_limit;
}
if (info->kmem_limit > sysmem_limit) {
info->kmem_limit = sysmem_limit;
}
}
info->cpu_system_use = sys_cpu_usage;
info->online_cpus = (uint32_t)get_nprocs();
info->image_type = util_strdup_s(cont->common_config->image_type);
if (copy_map_labels(cont->common_config->config, &map_labels) != 0) {
ret = -1;
goto cleanup;
}
if (!filters_args_match(ctx->stats_filters, "id", info->id)) {
ret = -1;
goto cleanup;
}
// Do not include container if any of the labels don't match
if (!filters_args_match_kv_list(ctx->stats_filters, "label", map_labels)) {
ret = -1;
goto cleanup;
}
cleanup:
map_free(map_labels);
if (ret != 0) {
free_container_info(info);
info = NULL;
}
return info;
}
static struct stats_context *fold_stats_filter(const container_stats_request *request)
{
size_t i, j;
struct stats_context *ctx = NULL;
ctx = stats_context_new(request);
if (ctx == NULL) {
ERROR("Out of memory");
return NULL;
}
if (request->containers != NULL && request->containers_len > 0) {
ctx->stats_config->all = true;
}
if (request->filters == NULL) {
return ctx;
}
for (i = 0; i < request->filters->len; i++) {
if (!filters_args_valid_key(accepted_stats_filter_tags, sizeof(accepted_stats_filter_tags) / sizeof(char *),
request->filters->keys[i])) {
ERROR("Invalid filter '%s'", request->filters->keys[i]);
lcrd_set_error_message("Invalid filter '%s'", request->filters->keys[i]);
goto error_out;
}
for (j = 0; j < request->filters->values[i]->len; j++) {
bool bret = false;
bret = filters_args_add(ctx->stats_filters, request->filters->keys[i],
request->filters->values[i]->keys[j]);
if (!bret) {
ERROR("Add filter args failed");
goto error_out;
}
}
}
return ctx;
error_out:
free_stats_context(ctx);
return NULL;
}
static int service_stats_make_memory(container_info ***stats_arr, size_t num)
{
if (num > SIZE_MAX / sizeof(container_info *)) {
return -1;
}
*stats_arr = util_common_calloc_s(num * sizeof(container_info *));
if (*stats_arr == NULL) {
ERROR("Out of memory");
return -1;
}
return 0;
}
static void pack_stats_response(container_stats_response *response, uint32_t cc, size_t info_len,
container_info **info)
{
if (response == NULL) {
return;
}
response->container_stats_len = info_len;
response->container_stats = info;
response->cc = cc;
if (g_lcrd_errmsg != NULL) {
response->errmsg = util_strdup_s(g_lcrd_errmsg);
DAEMON_CLEAR_ERRMSG();
}
}
static int stats_get_all_containers_id(const container_stats_request *request, char ***idsarray, size_t *ids_len,
bool *check_exists)
{
int ret = -1;
char **array = NULL;
if (request == NULL) {
return 0;
}
if (request->containers_len > 0 && request->containers != NULL) {
size_t n;
for (n = 0; n < request->containers_len; n++) {
if (!util_valid_container_id_or_name(request->containers[n])) {
ERROR("Invalid container name: %s", request->containers[n]);
lcrd_set_error_message("Invalid container name: %s", request->containers[n]);
goto cleanup;
}
if (util_array_append(&array, request->containers[n]) != 0) {
ERROR("Can not append array");
goto cleanup;
}
}
*check_exists = true;
} else {
array = containers_store_list_ids();
}
*ids_len = util_array_len((const char **)array);
*idsarray = array;
array = NULL;
ret = 0;
cleanup:
util_free_array(array);
return ret;
}
static int get_containers_stats(const char *runtime, char **idsarray, size_t ids_len, const struct stats_context *ctx,
bool check_exists, container_info ***info, size_t *info_len)
{
int ret = 0;
int nret;
size_t i;
char *engine_path = NULL;
struct engine_operation *engine_ops = NULL;
engine_ops = engines_get_handler(runtime);
if (engine_ops == NULL || engine_ops->engine_get_container_status_op == NULL ||
engine_ops->engine_free_container_status_op == NULL) {
ERROR("Failed to get engine stats operations");
ret = -1;
goto cleanup;
}
engine_path = conf_get_routine_rootdir(runtime);
if (engine_path == NULL) {
ERROR("Get engine path failed");
ret = -1;
goto cleanup;
}
nret = service_stats_make_memory(info, ids_len);
if (nret != 0) {
ret = -1;
goto cleanup;
}
for (i = 0; i < ids_len; i++) {
struct engine_container_info einfo = { 0 };
container_t *cont = NULL;
cont = containers_store_get(idsarray[i]);
if (cont == NULL) {
if (check_exists) {
ERROR("No such container: %s", idsarray[i]);
lcrd_set_error_message("No such container: %s", idsarray[i]);
ret = -1;
goto cleanup;
}
continue;
}
if (is_running(cont->state)) {
nret = engine_ops->engine_get_container_status_op(cont->common_config->id, engine_path, &einfo);
if (nret != 0) {
engine_ops->engine_clear_errmsg_op();
container_unref(cont);
continue;
}
} else {
if (!ctx->stats_config->all) {
container_unref(cont);
continue;
}
}
(*info)[*info_len] = get_container_stats(cont, &einfo, ctx);
container_unref(cont);
engine_ops->engine_free_container_status_op(&einfo);
if ((*info)[*info_len] == NULL) {
continue;
}
(*info_len)++;
}
cleanup:
free(engine_path);
return ret;
}
static int container_stats_cb(const container_stats_request *request,
container_stats_response **response)
{
bool check_exists = false;
size_t ids_len = 0;
size_t info_len = 0;
uint32_t cc = LCRD_SUCCESS;
char **idsarray = NULL;
container_info **info = NULL;
struct stats_context *ctx = NULL;
DAEMON_CLEAR_ERRMSG();
if (request == NULL || response == NULL) {
ERROR("Invalid NULL input");
return -1;
}
*response = util_common_calloc_s(sizeof(container_stats_response));
if (*response == NULL) {
ERROR("Out of memory");
cc = LCRD_ERR_MEMOUT;
goto pack_response;
}
if (request->runtime == NULL) {
ERROR("Receive NULL Request runtime");
cc = LCRD_ERR_INPUT;
goto pack_response;
}
ctx = fold_stats_filter(request);
if (ctx == NULL) {
cc = LCRD_ERR_EXEC;
goto pack_response;
}
if (stats_get_all_containers_id(request, &idsarray, &ids_len, &check_exists) != 0) {
cc = LCRD_ERR_EXEC;
goto pack_response;
}
if (ids_len == 0) {
goto pack_response;
}
if (get_containers_stats(request->runtime, idsarray, ids_len, ctx, check_exists, &info, &info_len)) {
cc = LCRD_ERR_EXEC;
goto pack_response;
}
pack_response:
pack_stats_response(*response, cc, info_len, info);
util_free_array(idsarray);
free_stats_context(ctx);
return (cc == LCRD_SUCCESS) ? 0 : -1;
}
static int runtime_resume(const char *id, const char *runtime, const char *rootpath)
{
int ret = 0;
struct engine_operation *engine_ops = NULL;
engine_ops = engines_get_handler(runtime);
if (engine_ops == NULL || engine_ops->engine_resume_op == NULL) {
DEBUG("Failed to get engine resume operations");
ret = -1;
goto out;
}
if (!engine_ops->engine_resume_op(id, rootpath)) {
DEBUG("Resume container %s failed", id);
const char *tmpmsg = NULL;
tmpmsg = engine_ops->engine_get_errmsg_op();
lcrd_set_error_message("Resume container error;%s",
(tmpmsg && strcmp(tmpmsg, DEF_SUCCESS_STR)) ? tmpmsg
: DEF_ERR_RUNTIME_STR);
engine_ops->engine_clear_errmsg_op();
ret = -1;
goto out;
}
out:
return ret;
}
static int resume_container(container_t *cont)
{
int ret = 0;
const char *id = cont->common_config->id;
container_lock(cont);
if (!is_running(cont->state)) {
ERROR("Container %s is not running", id);
lcrd_set_error_message("Container %s is not running", id);
ret = -1;
goto out;
}
if (!is_paused(cont->state)) {
ERROR("Container %s is not paused", id);
lcrd_set_error_message("Container %s is not paused", id);
ret = -1;
goto out;
}
if (runtime_resume(id, cont->runtime, cont->root_path)) {
ERROR("Failed to resume container:%s", id);
ret = -1;
goto out;
}
state_reset_paused(cont->state);
if (container_to_disk(cont)) {
ERROR("Failed to save container \"%s\" to disk", id);
ret = -1;
goto out;
}
out:
container_unlock(cont);
return ret;
}
#ifdef ENABLE_OCI_IMAGE
static int oci_image_export_rootfs(const char *id, const char *file)
{
int ret = 0;
rootfs_export_request *request = NULL;
rootfs_export_response *response = NULL;
if (id == NULL || file == NULL) {
ERROR("Invalid input arguments");
ret = -1;
goto out;
}
request = util_common_calloc_s(sizeof(rootfs_export_request));
if (request == NULL) {
ERROR("Memory out");
ret = -1;
goto out;
}
request->id = util_strdup_s(id);
request->file = util_strdup_s(file);
ret = export_rootfs(request, &response);
if (ret != 0) {
ERROR("Failed to export rootfs to %s from container %s", file, id);
ret = -1;
goto out;
}
out:
free_rootfs_export_request(request);
free_rootfs_export_response(response);
return ret;
}
#endif
static int export_container(container_t *cont, const char *file)
{
int ret = 0;
container_lock(cont);
#ifdef ENABLE_OCI_IMAGE
if (oci_image_export_rootfs(cont->common_config->id, file)) {
ret = -1;
}
#endif
container_unlock(cont);
return ret;
}
static void pack_resume_response(container_resume_response *response, uint32_t cc, const char *id)
{
if (response == NULL) {
return;
}
response->cc = cc;
if (id != NULL) {
response->id = util_strdup_s(id);
}
if (g_lcrd_errmsg != NULL) {
response->errmsg = util_strdup_s(g_lcrd_errmsg);
DAEMON_CLEAR_ERRMSG();
}
}
static int container_resume_cb(const container_resume_request *request, container_resume_response **response)
{
int ret = 0;
uint32_t cc = LCRD_SUCCESS;
char *name = NULL;
char *id = NULL;
container_t *cont = NULL;
DAEMON_CLEAR_ERRMSG();
if (request == NULL || response == NULL) {
ERROR("Invalid NULL input");
return -1;
}
*response = util_common_calloc_s(sizeof(container_resume_response));
if (*response == NULL) {
ERROR("Resume: Out of memory");
cc = LCRD_ERR_MEMOUT;
goto pack_response;
}
name = request->id;
if (name == NULL) {
ERROR("Resume: receive NULL id");
cc = LCRD_ERR_INPUT;
goto pack_response;
}
if (!util_valid_container_id_or_name(name)) {
ERROR("Invalid container name %s", name);
lcrd_set_error_message("Invalid container name %s", name);
cc = LCRD_ERR_EXEC;
goto pack_response;
}
cont = containers_store_get(name);
if (cont == NULL) {
ERROR("No such container:%s", name);
lcrd_set_error_message("No such container:%s", name);
cc = LCRD_ERR_EXEC;
goto pack_response;
}
id = cont->common_config->id;
set_log_prefix(id);
EVENT("Event: {Object: %s, Type: Resuming}", id);
if (gc_is_gc_progress(id)) {
lcrd_set_error_message("You cannot resume container %s in garbage collector progress.", id);
ERROR("You cannot resume container %s in garbage collector progress.", id);
cc = LCRD_ERR_EXEC;
goto pack_response;
}
ret = resume_container(cont);
if (ret != 0) {
cc = LCRD_ERR_EXEC;
container_state_set_error(cont->state, (const char *)g_lcrd_errmsg);
goto pack_response;
}
EVENT("Event: {Object: %s, Type: Resumed}", id);
pack_response:
pack_resume_response(*response, cc, id);
container_unref(cont);
free_log_prefix();
return (cc == LCRD_SUCCESS) ? 0 : -1;
}
static int runtime_pause(const char *id, const char *runtime, const char *rootpath)
{
int ret = 0;
struct engine_operation *engine_ops = NULL;
engine_ops = engines_get_handler(runtime);
if (engine_ops == NULL || engine_ops->engine_pause_op == NULL) {
DEBUG("Failed to get engine pause operations");
ret = -1;
goto out;
}
if (!engine_ops->engine_pause_op(id, rootpath)) {
DEBUG("Pause container %s failed", id);
const char *tmpmsg = NULL;
tmpmsg = engine_ops->engine_get_errmsg_op();
lcrd_set_error_message("Pause container error;%s", (tmpmsg && strcmp(tmpmsg, DEF_SUCCESS_STR)) ?
tmpmsg : DEF_ERR_RUNTIME_STR);
engine_ops->engine_clear_errmsg_op();
ret = -1;
goto out;
}
out:
return ret;
}
static int pause_container(container_t *cont)
{
int ret = 0;
const char *id = cont->common_config->id;
container_lock(cont);
if (!is_running(cont->state)) {
ERROR("Container %s is not running", id);
lcrd_set_error_message("Container %s is not running", id);
ret = -1;
goto out;
}
if (is_paused(cont->state)) {
ERROR("Container %s is already paused", id);
lcrd_set_error_message("Container %s is already paused", id);
ret = -1;
goto out;
}
if (is_restarting(cont->state)) {
ERROR("Container %s is restarting, wait until the container is running", id);
lcrd_set_error_message("Container %s is restarting, wait until the container is running", id);
ret = -1;
goto out;
}
if (runtime_pause(id, cont->runtime, cont->root_path)) {
ERROR("Failed to pause container:%s", id);
ret = -1;
goto out;
}
state_set_paused(cont->state);
if (container_to_disk(cont)) {
ERROR("Failed to save container \"%s\" to disk", id);
ret = -1;
goto out;
}
out:
container_unlock(cont);
return ret;
}
static void pack_pause_response(container_pause_response *response, uint32_t cc, const char *id)
{
if (response == NULL) {
return;
}
response->cc = cc;
if (id != NULL) {
response->id = util_strdup_s(id);
}
if (g_lcrd_errmsg != NULL) {
response->errmsg = util_strdup_s(g_lcrd_errmsg);
DAEMON_CLEAR_ERRMSG();
}
}
static int container_pause_cb(const container_pause_request *request,
container_pause_response **response)
{
int ret = 0;
uint32_t cc = LCRD_SUCCESS;
char *name = NULL;
char *id = NULL;
container_t *cont = NULL;
DAEMON_CLEAR_ERRMSG();
if (request == NULL || response == NULL) {
ERROR("Invalid NULL input");
return -1;
}
*response = util_common_calloc_s(sizeof(container_pause_response));
if (*response == NULL) {
ERROR("Pause: Out of memory");
cc = LCRD_ERR_MEMOUT;
goto pack_response;
}
name = request->id;
if (name == NULL) {
ERROR("Pause: receive NULL id");
cc = LCRD_ERR_INPUT;
goto pack_response;
}
if (!util_valid_container_id_or_name(name)) {
ERROR("Invalid container name %s", name);
lcrd_set_error_message("Invalid container name %s", name);
cc = LCRD_ERR_EXEC;
goto pack_response;
}
cont = containers_store_get(name);
if (cont == NULL) {
ERROR("No such container:%s", name);
lcrd_set_error_message("No such container:%s", name);
cc = LCRD_ERR_EXEC;
goto pack_response;
}
id = cont->common_config->id;
set_log_prefix(id);
EVENT("Event: {Object: %s, Type: Pausing}", id);
if (gc_is_gc_progress(id)) {
lcrd_set_error_message("You cannot pause container %s in garbage collector progress.", id);
ERROR("You cannot pause container %s in garbage collector progress.", id);
cc = LCRD_ERR_EXEC;
goto pack_response;
}
ret = pause_container(cont);
if (ret != 0) {
cc = LCRD_ERR_EXEC;
container_state_set_error(cont->state, (const char *)g_lcrd_errmsg);
goto pack_response;
}
EVENT("Event: {Object: %s, Type: Paused}", id);
update_health_monitor(id);
pack_response:
pack_pause_response(*response, cc, id);
container_unref(cont);
free_log_prefix();
return (cc == LCRD_SUCCESS) ? 0 : -1;
}
static void to_engine_resources(const host_config *hostconfig, struct engine_cgroup_resources *cr)
{
if (hostconfig == NULL || cr == NULL) {
return;
}
cr->blkio_weight = hostconfig->blkio_weight;
cr->cpu_shares = (uint64_t)hostconfig->cpu_shares;
cr->cpu_period = (uint64_t)hostconfig->cpu_period;
cr->cpu_quota = (uint64_t)hostconfig->cpu_quota;
cr->cpuset_cpus = hostconfig->cpuset_cpus;
cr->cpuset_mems = hostconfig->cpuset_mems;
cr->memory_limit = (uint64_t)hostconfig->memory;
cr->memory_swap = (uint64_t)hostconfig->memory_swap;
cr->memory_reservation = (uint64_t)hostconfig->memory_reservation;
cr->kernel_memory_limit = (uint64_t)hostconfig->kernel_memory;
}
static void host_config_restore_unlocking(container_t *cont, host_config *backup_hostconfig)
{
free_host_config(cont->hostconfig);
cont->hostconfig = backup_hostconfig;
if (container_to_disk(cont)) {
ERROR("Failed to save container \"%s\" to disk", cont->common_config->id);
}
}
static void update_container_cpu(const host_config *hostconfig, host_config *chostconfig)
{
if (hostconfig->cpu_shares != 0) {
chostconfig->cpu_shares = hostconfig->cpu_shares;
}
if (hostconfig->cpu_period != 0) {
chostconfig->cpu_period = hostconfig->cpu_period;
}
if (hostconfig->cpu_quota != 0) {
chostconfig->cpu_quota = hostconfig->cpu_quota;
}
if (hostconfig->cpuset_cpus != NULL) {
free(chostconfig->cpuset_cpus);
chostconfig->cpuset_cpus = util_strdup_s(hostconfig->cpuset_cpus);
}
if (hostconfig->cpuset_mems != NULL) {
free(chostconfig->cpuset_mems);
chostconfig->cpuset_mems = util_strdup_s(hostconfig->cpuset_mems);
}
}
static int update_container_memory(const char *id, const host_config *hostconfig, host_config *chostconfig)
{
int ret = 0;
if (hostconfig->memory != 0) {
// if memory limit smaller than already set memoryswap limit and doesn't
// update the memoryswap limit, then error out.
if (chostconfig->memory_swap > 0 &&
hostconfig->memory > chostconfig->memory_swap &&
hostconfig->memory_swap == 0) {
ERROR("Memory limit should be smaller than already set memoryswap limit,"
" update the memoryswap at the same time");
lcrd_set_error_message("Cannot update container %s: Memory limit should be smaller than"
"already set memoryswap limit, update the memoryswap at the same time.", id);
ret = -1;
goto out;
}
chostconfig->memory = hostconfig->memory;
}
if (hostconfig->memory_swap != 0) {
chostconfig->memory_swap = hostconfig->memory_swap;
}
if (hostconfig->memory_reservation != 0) {
chostconfig->memory_reservation = hostconfig->memory_reservation;
}
if (hostconfig->kernel_memory != 0) {
chostconfig->kernel_memory = hostconfig->kernel_memory;
}
out:
return ret;
}
static int update_container_restart_policy(const host_config *hostconfig, host_config *chostconfig)
{
int ret = 0;
if (hostconfig->restart_policy != NULL && hostconfig->restart_policy->name != NULL) {
free_host_config_restart_policy(chostconfig->restart_policy);
chostconfig->restart_policy = util_common_calloc_s(sizeof(host_config_restart_policy));
if (chostconfig->restart_policy == NULL) {
ERROR("Out of memory");
ret = -1;
goto out;
}
chostconfig->restart_policy->name = util_strdup_s(hostconfig->restart_policy->name);
chostconfig->restart_policy->maximum_retry_count = hostconfig->restart_policy->maximum_retry_count;
}
out:
return ret;
}
static int update_container(const container_t *cont, const host_config *hostconfig)
{
int ret = 0;
char *id = NULL;
host_config *chostconfig = NULL;
if (cont == NULL || cont->hostconfig == NULL || hostconfig == NULL) {
return -1;
}
id = cont->common_config->id;
chostconfig = cont->hostconfig;
if (hostconfig->blkio_weight != 0) {
chostconfig->blkio_weight = hostconfig->blkio_weight;
}
update_container_cpu(hostconfig, chostconfig);
ret = update_container_memory(id, hostconfig, chostconfig);
if (ret != 0) {
ret = -1;
goto out;
}
ret = update_container_restart_policy(hostconfig, chostconfig);
if (ret != 0) {
ret = -1;
goto out;
}
out:
return ret;
}
host_config *dump_host_config(const host_config *origconfig)
{
char *json = NULL;
parser_error err = NULL;
host_config *newconfig = NULL;
if (origconfig == NULL) {
return NULL;
}
json = host_config_generate_json(origconfig, NULL, &err);
if (json == NULL) {
ERROR("Failed to generate json: %s", err);
goto out;
}
newconfig = host_config_parse_data(json, NULL, &err);
if (newconfig == NULL) {
ERROR("Failed to parse json: %s", err);
goto out;
}
out:
free(err);
free(json);
return newconfig;
}
static int update_host_config_check(container_t *cont, host_config *hostconfig)
{
int ret = 0;
const char *id = cont->common_config->id;
ret = verify_host_config_settings(hostconfig, true);
if (ret != 0) {
goto out;
}
if (is_removal_in_progress(cont->state) || is_dead(cont->state)) {
ERROR("Container is marked for removal and cannot be \"update\".");
lcrd_set_error_message("Cannot update container %s: Container is marked for removal and cannot be \"update\".",
id);
ret = -1;
goto out;
}
if (is_running(cont->state) && hostconfig->kernel_memory) {
ERROR("Can not update kernel memory to a running container, please stop it first.");
lcrd_set_error_message("Cannot update container %s: Can not update kernel memory to a running container,"
" please stop it first.", id);
ret = -1;
goto out;
}
out:
return ret;
}
static int runtime_update(const char *id, const char *runtime, const char *rootpath, struct engine_cgroup_resources *cr)
{
int ret = 0;
struct engine_operation *engine_ops = NULL;
engine_ops = engines_get_handler(runtime);
if (engine_ops == NULL || engine_ops->engine_update_op == NULL) {
DEBUG("Failed to get engine update operations");
ret = -1;
goto out;
}
if (!engine_ops->engine_update_op(id, rootpath, cr)) {
DEBUG("Update container %s failed", id);
const char *tmpmsg = NULL;
tmpmsg = engine_ops->engine_get_errmsg_op();
lcrd_set_error_message("Cannot update container %s: %s", id, (tmpmsg && strcmp(tmpmsg, DEF_SUCCESS_STR)) ?
tmpmsg : DEF_ERR_RUNTIME_STR);
engine_ops->engine_clear_errmsg_op();
ret = -1;
goto out;
}
out:
return ret;
}
static int do_update_resources(const container_update_request *request, container_t *cont)
{
int ret = 0;
const char *id = cont->common_config->id;
parser_error err = NULL;
host_config *hostconfig = NULL;
host_config *backup_hostconfig = NULL;
struct engine_cgroup_resources cr = { 0 };
if (request->host_config == NULL) {
DEBUG("receive NULL host config");
ret = -1;
goto out;
}
hostconfig = host_config_parse_data(request->host_config, NULL, &err);
if (hostconfig == NULL) {
ERROR("Failed to parse host config data:%s", err);
ret = -1;
goto out;
}
container_lock(cont);
if (update_host_config_check(cont, hostconfig)) {
ret = -1;
goto unlock_out;
}
backup_hostconfig = dump_host_config(cont->hostconfig);
if (backup_hostconfig == NULL) {
ret = -1;
goto unlock_out;
}
if (update_container(cont, hostconfig)) {
host_config_restore_unlocking(cont, backup_hostconfig);
ret = -1;
goto unlock_out;
}
if (container_to_disk(cont)) {
ERROR("Failed to save container \"%s\" to disk", id);
host_config_restore_unlocking(cont, backup_hostconfig);
ret = -1;
goto unlock_out;
}
if (hostconfig->restart_policy && hostconfig->restart_policy->name) {
container_update_restart_manager(cont, hostconfig->restart_policy);
}
to_engine_resources(hostconfig, &cr);
if (runtime_update(id, cont->runtime, cont->root_path, &cr)) {
ERROR("Update container %s failed", id);
host_config_restore_unlocking(cont, backup_hostconfig);
ret = -1;
goto unlock_out;
}
unlock_out:
container_unlock(cont);
out:
if (ret == 0) {
free_host_config(backup_hostconfig);
}
free_host_config(hostconfig);
free(err);
return ret;
}
static void pack_update_response(container_update_response *response, uint32_t cc, const char *id)
{
if (response == NULL) {
return;
}
response->cc = cc;
if (id != NULL) {
response->id = util_strdup_s(id);
}
if (g_lcrd_errmsg != NULL) {
response->errmsg = util_strdup_s(g_lcrd_errmsg);
DAEMON_CLEAR_ERRMSG();
}
}
static int container_update_cb(const container_update_request *request, container_update_response **response)
{
uint32_t cc = LCRD_SUCCESS;
char *container_name = NULL;
char *id = NULL;
container_t *cont = NULL;
DAEMON_CLEAR_ERRMSG();
if (request == NULL || response == NULL) {
ERROR("Invalid NULL input");
return -1;
}
*response = util_common_calloc_s(sizeof(container_update_response));
if (*response == NULL) {
ERROR("Out of memory");
cc = LCRD_ERR_MEMOUT;
goto pack_response;
}
container_name = request->name;
if (container_name == NULL) {
DEBUG("receive NULL Request id");
cc = LCRD_ERR_INPUT;
goto pack_response;
}
if (!util_valid_container_id_or_name(container_name)) {
ERROR("Invalid container name %s", container_name);
lcrd_set_error_message("Invalid container name %s", container_name);
cc = LCRD_ERR_EXEC;
goto pack_response;
}
cont = containers_store_get(container_name);
if (cont == NULL) {
ERROR("No such container: %s", container_name);
lcrd_set_error_message("No such container: %s", container_name);
cc = LCRD_ERR_EXEC;
goto pack_response;
}
id = cont->common_config->id;
set_log_prefix(id);
EVENT("Event: {Object: %s, Type: updating}", id);
if (do_update_resources(request, cont) != 0) {
cc = LCRD_ERR_EXEC;
goto pack_response;
}
EVENT("Event: {Object: %s, Type: updated}", id);
pack_response:
pack_update_response(*response, cc, id);
container_unref(cont);
free_log_prefix();
return (cc == LCRD_SUCCESS) ? 0 : -1;
}
static void pack_export_response(container_export_response *response, uint32_t cc, const char *id)
{
if (response == NULL) {
return;
}
response->cc = cc;
if (id != NULL) {
response->id = util_strdup_s(id);
}
if (g_lcrd_errmsg != NULL) {
response->errmsg = util_strdup_s(g_lcrd_errmsg);
DAEMON_CLEAR_ERRMSG();
}
}
static int container_export_cb(const container_export_request *request, container_export_response **response)
{
int ret = 0;
char *name = NULL;
char *id = NULL;
char *file = NULL;
uint32_t cc = LCRD_SUCCESS;
container_t *cont = NULL;
DAEMON_CLEAR_ERRMSG();
if (request == NULL || response == NULL) {
ERROR("Invalid NULL input");
return -1;
}
*response = util_common_calloc_s(sizeof(container_export_response));
if (*response == NULL) {
ERROR("Export: Out of memory");
cc = LCRD_ERR_MEMOUT;
goto pack_response;
}
name = request->id;
file = request->file;
if (name == NULL) {
ERROR("Export: receive NULL id");
cc = LCRD_ERR_INPUT;
goto pack_response;
}
if (!util_valid_container_id_or_name(name)) {
ERROR("Invalid container name %s", name);
lcrd_set_error_message("Invalid container name %s", name);
cc = LCRD_ERR_EXEC;
goto pack_response;
}
cont = containers_store_get(name);
if (cont == NULL) {
ERROR("No such container:%s", name);
lcrd_set_error_message("No such container:%s", name);
cc = LCRD_ERR_EXEC;
goto pack_response;
}
id = cont->common_config->id;
set_log_prefix(id);
if (gc_is_gc_progress(id)) {
lcrd_set_error_message("You cannot export container %s in garbage collector progress.", id);
ERROR("You cannot export container %s in garbage collector progress.", id);
cc = LCRD_ERR_EXEC;
goto pack_response;
}
ret = export_container(cont, file);
if (ret != 0) {
cc = LCRD_ERR_EXEC;
goto pack_response;
}
pack_response:
pack_export_response(*response, cc, id);
container_unref(cont);
free_log_prefix();
return (cc == LCRD_SUCCESS) ? 0 : -1;
}
void container_extend_callback_init(service_container_callback_t *cb)
{
cb->update = container_update_cb;
cb->pause = container_pause_cb;
cb->resume = container_resume_cb;
cb->stats = container_stats_cb;
cb->events = container_events_cb;
cb->export_rootfs = container_export_cb;
}