/****************************************************************************** * Copyright (c) Huawei Technologies Co., Ltd. 2018-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: lifeng * Create: 2018-11-08 * Description: provide container configure definition ******************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include "constants.h" #include "log.h" #include "utils.h" #include "lcrd_config.h" #include "sysinfo.h" #include "liblcrd.h" #define ENGINE_ROOTPATH_NAME "engines" #define GRAPH_ROOTPATH_NAME "storage" #define GRAPH_ROOTPATH_CHECKED_FLAG "NEED_CHECK" #define INCREMENT_INTREVAL 2 #define BUFFER_ITEM_NUMS 10 static struct lcrd_conf g_lcrd_conf; static double g_jiffy = 0.0; /* tick to ns */ static inline unsigned long long tick_to_ns(uint64_t tick) { #define EPSINON 0.0001 if (g_jiffy < EPSINON && g_jiffy > -EPSINON) { g_jiffy = (double)sysconf(_SC_CLK_TCK); } if ((uint64_t)(tick / g_jiffy) > (UINT64_MAX / Time_Second)) { return UINT64_MAX; } return (uint64_t)((tick / g_jiffy) * Time_Second); } /* * returns the host system's cpu usage in nanoseconds. * Uses /proc/stat defined by POSIX. Looks for the cpu statistics line * and then sums up the first seven fields provided. * See `man 5 proc` for details on specific field information. */ int get_system_cpu_usage(uint64_t *val) { int ret = 0; int nret; unsigned long long total, usertime, nicetime, systemtime, idletime; unsigned long long ioWait, irq, softIrq, steal, guest, guestnice; char buffer[BUFSIZ + 1] = { 0 }; char *tmp = NULL; FILE *file = NULL; if (val == NULL) { return -1; } file = util_fopen("/proc/stat", "r"); if (file == NULL) { ERROR("Failed to open '/proc/stat'"); return -1; } ioWait = irq = softIrq = steal = guest = guestnice = 0; /* * Depending on your kernel version, * 5, 7, 8 or 9 of these fields will be set. * The rest will remain at zero. */ tmp = fgets(buffer, BUFSIZ, file); if (tmp == NULL) { ret = -1; goto out; } nret = sscanf(buffer, "cpu %16llu %16llu %16llu %16llu %16llu %16llu %16llu %16llu %16llu %16llu", &usertime, &nicetime, &systemtime, &idletime, &ioWait, &irq, &softIrq, &steal, &guest, &guestnice); if (nret != BUFFER_ITEM_NUMS) { ERROR("sscanf buffer failed"); ret = -1; goto out; } total = usertime + nicetime + systemtime + idletime + ioWait + irq + softIrq; *val = tick_to_ns(total); out: fclose(file); return ret; } /* lcrd server conf wrlock */ int lcrd_server_conf_wrlock() { int ret = 0; if (pthread_rwlock_wrlock(&g_lcrd_conf.lcrd_conf_rwlock)) { ERROR("Failed to acquire lcrd conf write lock"); ret = -1; } return ret; } /* lcrd server conf rdlock */ int lcrd_server_conf_rdlock() { int ret = 0; if (pthread_rwlock_rdlock(&g_lcrd_conf.lcrd_conf_rwlock)) { ERROR("Failed to acquire lcrd conf read lock"); ret = -1; } return ret; } /* lcrd server conf unlock */ int lcrd_server_conf_unlock() { int ret = 0; if (pthread_rwlock_unlock(&g_lcrd_conf.lcrd_conf_rwlock)) { ERROR("Failed to release lcrd conf lock"); ret = -1; } return ret; } struct service_arguments *conf_get_server_conf() { return g_lcrd_conf.server_conf; } /* conf get lcrd pidfile */ char *conf_get_lcrd_pidfile() { char *filename = NULL; struct service_arguments *conf = NULL; if (lcrd_server_conf_rdlock() != 0) { return NULL; } conf = conf_get_server_conf(); if (conf == NULL || conf->json_confs == NULL || conf->json_confs->pidfile == NULL) { goto out; } filename = util_strdup_s(conf->json_confs->pidfile); out: (void)lcrd_server_conf_unlock(); return filename; } /* conf get engine rootpath */ char *conf_get_engine_rootpath() { char *epath = NULL; char *rootpath = NULL; size_t len; rootpath = conf_get_lcrd_rootdir(); if (rootpath == NULL) { ERROR("Get rootpath failed"); return epath; } if (strlen(rootpath) > (SIZE_MAX - strlen(ENGINE_ROOTPATH_NAME)) - 2) { ERROR("Root path is too long"); goto free_out; } len = strlen(rootpath) + 1 + strlen(ENGINE_ROOTPATH_NAME) + 1; if (len > PATH_MAX) { ERROR("The size of path exceeds the limit"); goto free_out; } epath = util_common_calloc_s(len); if (epath == NULL) { ERROR("Out of memory"); goto free_out; } int nret = snprintf(epath, len, "%s/%s", rootpath, ENGINE_ROOTPATH_NAME); if (nret < 0 || (size_t)nret >= len) { ERROR("Sprintf engine path failed"); free(epath); epath = NULL; } free_out: free(rootpath); return epath; } /* conf get graph rootpath */ char *conf_get_graph_rootpath() { char *epath = NULL; char *rootpath = NULL; size_t len; rootpath = conf_get_lcrd_rootdir(); if (rootpath == NULL) { ERROR("Get rootpath failed"); return epath; } if (strlen(rootpath) > (SIZE_MAX - strlen(GRAPH_ROOTPATH_NAME)) - 2) { ERROR("Root path is too long"); goto free_out; } len = strlen(rootpath) + 1 + strlen(GRAPH_ROOTPATH_NAME) + 1; if (len > PATH_MAX) { ERROR("The size of path exceeds the limit"); goto free_out; } epath = util_common_calloc_s(len); if (epath == NULL) { ERROR("Out of memory"); goto free_out; } int nret = snprintf(epath, len, "%s/%s", rootpath, GRAPH_ROOTPATH_NAME); if (nret < 0 || (size_t)nret >= len) { ERROR("Sprintf graph path failed"); free(epath); epath = NULL; } free_out: free(rootpath); return epath; } /* conf get graph checked flag file path */ char *conf_get_graph_check_flag_file() { char *epath = NULL; char *rootpath = NULL; size_t len; rootpath = conf_get_lcrd_rootdir(); if (rootpath == NULL) { ERROR("Get rootpath failed"); return epath; } if (strlen(rootpath) > ((SIZE_MAX - strlen(GRAPH_ROOTPATH_NAME)) - strlen(GRAPH_ROOTPATH_CHECKED_FLAG)) - 3) { ERROR("Root path is too long"); goto free_out; } len = strlen(rootpath) + 1 + strlen(GRAPH_ROOTPATH_NAME) + 1 + strlen(GRAPH_ROOTPATH_CHECKED_FLAG) + 1; if (len > PATH_MAX) { ERROR("The size of path exceeds the limit"); goto free_out; } epath = util_common_calloc_s(len); if (epath == NULL) { ERROR("Out of memory"); goto free_out; } int nret = snprintf(epath, len, "%s/%s/%s", rootpath, GRAPH_ROOTPATH_NAME, GRAPH_ROOTPATH_CHECKED_FLAG); if (nret < 0 || (size_t)nret >= len) { ERROR("Sprintf graph checked flag failed"); free(epath); epath = NULL; } free_out: free(rootpath); return epath; } /* conf get graph run path */ char *conf_get_graph_run_path() { char *epath = NULL; char *rootpath = NULL; size_t len; rootpath = conf_get_lcrd_statedir(); if (rootpath == NULL) { ERROR("Get rootpath failed"); return epath; } if (strlen(rootpath) > (SIZE_MAX - strlen(GRAPH_ROOTPATH_NAME)) - 2) { ERROR("Root path is too long"); goto free_out; } len = strlen(rootpath) + 1 + strlen(GRAPH_ROOTPATH_NAME) + 1; if (len > PATH_MAX) { ERROR("The size of path exceeds the limit"); goto free_out; } epath = util_common_calloc_s(len); if (epath == NULL) { ERROR("Out of memory"); goto free_out; } int nret = snprintf(epath, len, "%s/%s", rootpath, GRAPH_ROOTPATH_NAME); if (nret < 0 || (size_t)nret >= len) { ERROR("Sprintf graph run path failed"); free(epath); epath = NULL; } free_out: free(rootpath); return epath; } /* conf get routine rootdir */ char *conf_get_routine_rootdir(const char *runtime) { char *path = NULL; struct service_arguments *conf = NULL; size_t len = 0; if (runtime == NULL) { ERROR("Runtime is NULL"); return NULL; } if (lcrd_server_conf_rdlock() != 0) { return NULL; } conf = conf_get_server_conf(); if (conf == NULL || conf->json_confs->graph == NULL) { ERROR("Server conf is NULL or rootpath is NULL"); goto out; } /* path = conf->rootpath + / + engines + / + runtime + /0 */ if (strlen(conf->json_confs->graph) > (SIZE_MAX - strlen(ENGINE_ROOTPATH_NAME)) - 3) { ERROR("Graph path is too long"); goto out; } len = strlen(conf->json_confs->graph) + 1 + strlen(ENGINE_ROOTPATH_NAME) + 1 + strlen(runtime) + 1; if (len > PATH_MAX / sizeof(char)) { ERROR("The size of path exceeds the limit"); goto out; } path = util_common_calloc_s(sizeof(char) * len); if (path == NULL) { ERROR("Out of memory"); goto out; } int nret = snprintf(path, len, "%s/%s/%s", conf->json_confs->graph, ENGINE_ROOTPATH_NAME, runtime); if (nret < 0 || (size_t)nret >= len) { ERROR("Failed to sprintf path"); free(path); path = NULL; } out: (void)lcrd_server_conf_unlock(); return path; } /* conf get routine statedir */ char *conf_get_routine_statedir(const char *runtime) { char *path = NULL; struct service_arguments *conf = NULL; size_t len = 0; if (runtime == NULL) { return NULL; } if (lcrd_server_conf_rdlock() != 0) { return NULL; } conf = conf_get_server_conf(); if (conf == NULL || conf->json_confs->state == NULL) { goto out; } /* path = conf->statepath + / + runtime + /0 */ if (strlen(conf->json_confs->state) > (SIZE_MAX - strlen(runtime)) - 2) { ERROR("State path is too long"); goto out; } len = strlen(conf->json_confs->state) + 1 + strlen(runtime) + 1; if (len > PATH_MAX) { goto out; } path = util_common_calloc_s(sizeof(char) * len); if (path == NULL) { goto out; } int nret = snprintf(path, len, "%s/%s", conf->json_confs->state, runtime); if (nret < 0 || (size_t)nret >= len) { ERROR("sprintf path failed"); free(path); path = NULL; } out: (void)lcrd_server_conf_unlock(); return path; } /* conf get lcrd rootdir */ char *conf_get_lcrd_rootdir() { char *path = NULL; struct service_arguments *conf = NULL; if (lcrd_server_conf_rdlock() != 0) { return NULL; } conf = conf_get_server_conf(); if (conf == NULL || conf->json_confs->graph == NULL) { goto out; } path = util_strdup_s(conf->json_confs->graph); out: (void)lcrd_server_conf_unlock(); return path; } #ifdef ENABLE_OCI_IMAGE /* conf get graph driver */ char *conf_get_lcrd_storage_driver() { char *driver = NULL; struct service_arguments *conf = NULL; if (lcrd_server_conf_rdlock() != 0) { return NULL; } conf = conf_get_server_conf(); if (conf == NULL || conf->driver->name == NULL) { goto out; } driver = util_strdup_s(conf->driver->name); out: (void)lcrd_server_conf_unlock(); return driver; } /* conf get graph driver */ char *conf_get_lcrd_storage_driver_backing_fs() { char *fs = NULL; struct service_arguments *conf = NULL; if (lcrd_server_conf_rdlock() != 0) { return NULL; } conf = conf_get_server_conf(); if (conf == NULL || conf->driver->backing_fs == NULL) { goto out; } fs = util_strdup_s(conf->driver->backing_fs); out: (void)lcrd_server_conf_unlock(); return fs; } /* conf get graph driver opts */ char **conf_get_storage_opts() { int nret = 0; size_t i; char **opts = NULL; char *p = NULL; struct service_arguments *conf = NULL; if (lcrd_server_conf_rdlock() != 0) { return NULL; } conf = conf_get_server_conf(); if (conf == NULL || conf->json_confs->storage_opts == NULL) { goto out; } for (i = 0; i < conf->json_confs->storage_opts_len; i++) { p = conf->json_confs->storage_opts[i]; if (p == NULL) { goto out; } nret = util_array_append(&opts, p); if (nret != 0) { ERROR("Out of memory"); goto out_free; } } out_free: if (nret != 0) { util_free_array(opts); opts = NULL; } out: (void)lcrd_server_conf_unlock(); return opts; } #endif /* conf get registry */ char **conf_get_registry_list() { int nret = 0; size_t i; char **opts = NULL; char *p = NULL; struct service_arguments *conf = NULL; if (lcrd_server_conf_rdlock() != 0) { return NULL; } conf = conf_get_server_conf(); if (conf == NULL || conf->json_confs->registry_mirrors_len == 0) { goto out; } for (i = 0; i < conf->json_confs->registry_mirrors_len; i++) { p = conf->json_confs->registry_mirrors[i]; if (p == NULL) { break; } nret = util_array_append(&opts, p); if (nret != 0) { ERROR("Out of memory"); util_free_array(opts); opts = NULL; goto out; } } out: (void)lcrd_server_conf_unlock(); return opts; } /* conf get insecure registry */ char **conf_get_insecure_registry_list() { int nret = 0; size_t i; char **opts = NULL; char *p = NULL; struct service_arguments *conf = NULL; if (lcrd_server_conf_rdlock() != 0) { return NULL; } conf = conf_get_server_conf(); if (conf == NULL || conf->json_confs->insecure_registries_len == 0) { goto out; } for (i = 0; i < conf->json_confs->insecure_registries_len; i++) { p = conf->json_confs->insecure_registries[i]; if (p == NULL) { break; } nret = util_array_append(&opts, p); if (nret != 0) { util_free_array(opts); opts = NULL; ERROR("Out of memory"); break; } } out: (void)lcrd_server_conf_unlock(); return opts; } /* conf get lcrd statedir */ char *conf_get_lcrd_statedir() { char *path = NULL; struct service_arguments *conf = NULL; if (lcrd_server_conf_rdlock() != 0) { return NULL; } conf = conf_get_server_conf(); if (conf == NULL || conf->json_confs->state == NULL) { goto out; } path = util_strdup_s(conf->json_confs->state); out: (void)lcrd_server_conf_unlock(); return path; } /* conf get lcrd mount rootfs */ char *conf_get_lcrd_mount_rootfs() { char *path = NULL; struct service_arguments *conf = NULL; if (lcrd_server_conf_rdlock() != 0) { return NULL; } conf = conf_get_server_conf(); if (conf == NULL || conf->json_confs->rootfsmntdir == NULL) { goto out; } path = util_strdup_s(conf->json_confs->rootfsmntdir); out: (void)lcrd_server_conf_unlock(); return path; } /* conf get lcrd umask for containers */ char *conf_get_lcrd_native_umask() { char *umask = NULL; struct service_arguments *conf = NULL; if (lcrd_server_conf_rdlock() != 0) { return NULL; } conf = conf_get_server_conf(); if (conf == NULL || conf->json_confs->native_umask == NULL) { goto out; } umask = util_strdup_s(conf->json_confs->native_umask); out: (void)lcrd_server_conf_unlock(); return umask; } /* conf get lcrd cgroup parent for containers */ char *conf_get_lcrd_cgroup_parent() { char *cgroup_parent = NULL; struct service_arguments *conf = NULL; if (lcrd_server_conf_rdlock() != 0) { return NULL; } conf = conf_get_server_conf(); if (conf == NULL || conf->json_confs->cgroup_parent == NULL) { goto out; } cgroup_parent = util_strdup_s(conf->json_confs->cgroup_parent); out: (void)lcrd_server_conf_unlock(); return cgroup_parent; } /* conf get lcrd engine */ char *conf_get_lcrd_engine() { char *engine = NULL; struct service_arguments *conf = NULL; if (lcrd_server_conf_rdlock() != 0) { return NULL; } conf = conf_get_server_conf(); if (conf == NULL || conf->json_confs->engine == NULL) { goto out; } engine = util_strdup_s(conf->json_confs->engine); out: (void)lcrd_server_conf_unlock(); return engine; } /* conf get lcrd loglevel */ char *conf_get_lcrd_loglevel() { char *loglevel = NULL; struct service_arguments *conf = NULL; if (lcrd_server_conf_rdlock() != 0) { return NULL; } conf = conf_get_server_conf(); if (conf == NULL || conf->json_confs->log_level == NULL) { goto out; } loglevel = util_strdup_s(conf->json_confs->log_level); out: (void)lcrd_server_conf_unlock(); return loglevel; } /* get log file helper */ char *get_log_file_helper(const struct service_arguments *conf, const char *suffix) { char *logfile = NULL; size_t len = 0; int nret = 0; if (suffix == NULL) { return NULL; } // log_file path = parent path + "/" + suffix if (strlen(conf->logpath) > (SIZE_MAX - strlen(suffix)) - 2) { ERROR("Log path is too long"); return NULL; } len = strlen(conf->logpath) + 1 + strlen(suffix) + 1; if (len > PATH_MAX) { ERROR("The size of path exceeds the limit"); return NULL; } logfile = util_common_calloc_s(len * sizeof(char)); if (logfile == NULL) { ERROR("Out of memory"); goto out; } nret = snprintf(logfile, len, "%s/%s", conf->logpath, suffix); if (nret < 0 || (size_t)nret >= len) { free(logfile); logfile = NULL; ERROR("Failed to sprintf log path"); } out: return logfile; } /* conf get lcrd log gather fifo path */ char *conf_get_lcrd_log_gather_fifo_path() { char *logfile = NULL; char *statedir = NULL; size_t len = 0; int nret; statedir = conf_get_lcrd_statedir(); if (statedir == NULL) { ERROR("Get lcrd statedir failed"); goto err_out; } if (strlen(statedir) > (SIZE_MAX - strlen("/lcrd_log_gather_fifo")) - 1) { ERROR("State path is too long"); goto err_out; } len = strlen(statedir) + strlen("/lcrd_log_gather_fifo") + 1; if (len > PATH_MAX) { ERROR("Too long path: %s", statedir); goto err_out; } logfile = util_common_calloc_s(len); if (logfile == NULL) { ERROR("Out of memory"); goto err_out; } nret = snprintf(logfile, len, "%s%s", statedir, "/lcrd_log_gather_fifo"); if (nret < 0 || (size_t)nret >= len) { ERROR("Sprintf log file failed"); goto err_out; } goto out; err_out: free(logfile); logfile = NULL; out: free(statedir); return logfile; } /* conf get lcrd log file */ char *conf_get_lcrd_log_file() { char *logfile = NULL; struct service_arguments *conf = NULL; if (lcrd_server_conf_rdlock() != 0) { return NULL; } conf = conf_get_server_conf(); if (conf == NULL || conf->logpath == NULL) { goto out; } logfile = get_log_file_helper(conf, "lcrd.log"); out: (void)lcrd_server_conf_unlock(); return logfile; } /* conf get engine log file */ char *conf_get_engine_log_file() { char *logfile = NULL; char *full_path = NULL; char *prefix = "fifo:"; size_t len = 0; logfile = conf_get_lcrd_log_gather_fifo_path(); if (logfile == NULL) { ERROR("conf_get_lcrd_log_gather_fifo_path failed"); goto out; } len = strlen(prefix) + strlen(logfile) + 1; if (len > PATH_MAX) { ERROR("The size of path exceeds the limit"); goto out; } full_path = util_common_calloc_s(len * sizeof(char)); if (full_path == NULL) { FATAL("Out of Memory"); goto out; } int nret = snprintf(full_path, len, "%s%s", prefix, logfile); if (nret < 0 || (size_t)nret >= len) { ERROR("Failed to sprintf engine log path"); free(full_path); full_path = NULL; goto out; } out: free(logfile); return full_path; } int conf_get_daemon_log_config(char **loglevel, char **logdriver, char **engine_log_path) { *loglevel = conf_get_lcrd_loglevel(); if (*loglevel == NULL) { ERROR("DoStart: Failed to get log level"); return -1; } *logdriver = conf_get_lcrd_logdriver(); if (*logdriver == NULL) { ERROR("DoStart: Failed to get log driver"); return -1; } *engine_log_path = conf_get_engine_log_file(); if (strcmp(*logdriver, "file") == 0 && *engine_log_path == NULL) { ERROR("DoStart: Log driver is file, but engine log path is NULL"); return -1; } return 0; } /* conf get lcrd logdriver */ char *conf_get_lcrd_logdriver() { char *logdriver = NULL; struct service_arguments *conf = NULL; if (lcrd_server_conf_rdlock() != 0) { return NULL; } conf = conf_get_server_conf(); if (conf == NULL || conf->json_confs->log_driver == NULL) { goto out; } logdriver = util_strdup_s(conf->json_confs->log_driver); out: (void)lcrd_server_conf_unlock(); return logdriver; } /* conf get image layer check flag */ bool conf_get_image_layer_check_flag() { bool check_flag = false; struct service_arguments *conf = NULL; if (lcrd_server_conf_rdlock() != 0) { return false; } conf = conf_get_server_conf(); if (conf == NULL) { goto out; } check_flag = conf->json_confs->image_layer_check; out: (void)lcrd_server_conf_unlock(); return check_flag; } /* conf get flag of use decrypted key to pull image */ bool conf_get_use_decrypted_key_flag() { bool check_flag = true; struct service_arguments *conf = NULL; if (lcrd_server_conf_rdlock() != 0) { return false; } conf = conf_get_server_conf(); if (conf == NULL || conf->json_confs->use_decrypted_key == NULL) { goto out; } check_flag = *(conf->json_confs->use_decrypted_key); out: (void)lcrd_server_conf_unlock(); return check_flag; } bool conf_get_skip_insecure_verify_flag() { bool check_flag = false; struct service_arguments *conf = NULL; if (lcrd_server_conf_rdlock() != 0) { return false; } conf = conf_get_server_conf(); if (conf == NULL) { goto out; } check_flag = conf->json_confs->insecure_skip_verify_enforce; out: (void)lcrd_server_conf_unlock(); return check_flag; } #define OCI_STR_ARRAY_DUP(src, dest, srclen, destlen, ret) \ do { \ if ((src) != NULL) { \ (dest) = str_array_dup((const char **)(src), (srclen)); \ if ((dest) == NULL) { \ (ret) = -1; \ goto out; \ } \ (destlen) = (srclen); \ } \ } while (0) #define HOOKS_ELEM_DUP_DEF(item) \ defs_hook *hooks_##item##_elem_dup(const defs_hook *src) \ { \ int ret = 0; \ defs_hook *dest = NULL; \ if (src == NULL) \ return NULL; \ dest = util_common_calloc_s(sizeof(defs_hook)); \ if (dest == NULL) \ return NULL; \ dest->path = util_strdup_s(src->path); \ OCI_STR_ARRAY_DUP(src->args, dest->args, src->args_len, dest->args_len, ret); \ OCI_STR_ARRAY_DUP(src->env, dest->env, src->env_len, dest->env_len, ret); \ dest->timeout = src->timeout; \ out: \ if (ret != 0 && dest != NULL) { \ free_defs_hook(dest); \ dest = NULL; \ } \ return dest; \ } /* HOOKS ELEM DUP DEF */ HOOKS_ELEM_DUP_DEF(prestart) /* HOOKS ELEM DUP DEF */ HOOKS_ELEM_DUP_DEF(poststart) /* HOOKS ELEM DUP DEF */ HOOKS_ELEM_DUP_DEF(poststop) #define HOOKS_ITEM_DUP_DEF(item) \ int hooks_##item##_dup(oci_runtime_spec_hooks *dest, const oci_runtime_spec_hooks *src) \ { \ int i = 0; \ if (src->item##_len > SIZE_MAX / sizeof(defs_hook *) - 1) { \ return -1; \ } \ dest->item = util_common_calloc_s(sizeof(defs_hook *) * (src->item##_len + 1)); \ if (dest->item == NULL) \ return -1; \ dest->item##_len = src->item##_len; \ for (; (size_t)i < src->item##_len; ++i) { \ dest->item[i] = hooks_##item##_elem_dup(src->item[i]); \ if (dest->item[i] == NULL) \ return -1; \ } \ return 0; \ } /* HOOKS ITEM DUP DEF */ HOOKS_ITEM_DUP_DEF(prestart) /* HOOKS ITEM DUP DEF */ HOOKS_ITEM_DUP_DEF(poststart) /* HOOKS ITEM DUP DEF */ HOOKS_ITEM_DUP_DEF(poststop) /* hooks_dup */ oci_runtime_spec_hooks *hooks_dup(const oci_runtime_spec_hooks *src) { int ret = 0; oci_runtime_spec_hooks *dest = NULL; if (src == NULL) { return NULL; } dest = util_common_calloc_s(sizeof(oci_runtime_spec_hooks)); if (dest == NULL) { return NULL; } ret = hooks_prestart_dup(dest, src); if (ret != 0) { goto out; } ret = hooks_poststart_dup(dest, src); if (ret != 0) { goto out; } ret = hooks_poststop_dup(dest, src); out: if (ret != 0) { free_oci_runtime_spec_hooks(dest); dest = NULL; } return dest; } /* conf get lcrd hooks */ int conf_get_lcrd_hooks(oci_runtime_spec_hooks **phooks) { int ret = 0; struct service_arguments *conf = NULL; if (lcrd_server_conf_rdlock() != 0) { return -1; } conf = conf_get_server_conf(); if (conf != NULL && conf->hooks != NULL) { *phooks = hooks_dup(conf->hooks); if ((*phooks) == NULL) { ret = -1; goto out; } } else { *phooks = NULL; } out: (void)lcrd_server_conf_unlock(); return ret; } /* conf get lcrd default ulimit */ int conf_get_lcrd_default_ulimit(host_config_ulimits_element ***ulimit) { int ret = 0; size_t i, ulimit_len; struct service_arguments *conf = NULL; if (lcrd_server_conf_rdlock() != 0) { return -1; } conf = conf_get_server_conf(); if (conf == NULL || conf->default_ulimit_len == 0) { *ulimit = NULL; goto out; } for (i = 0; i < conf->default_ulimit_len; i++) { ulimit_len = ulimit_array_len(*ulimit); if (ulimit_array_append(ulimit, conf->default_ulimit[i], ulimit_len) != 0) { ERROR("ulimit append failed"); ret = -1; goto out; } } out: (void)lcrd_server_conf_unlock(); return ret; } /* conf get start timeout */ unsigned int conf_get_start_timeout() { struct service_arguments *conf = NULL; unsigned int ret = 0; if (lcrd_server_conf_rdlock() != 0) { return 0; } conf = conf_get_server_conf(); if (conf == NULL) { goto out; } ret = conf->start_timeout; out: (void)lcrd_server_conf_unlock(); return ret; } /* conf get image opt timeout */ unsigned int conf_get_im_opt_timeout() { struct service_arguments *conf = NULL; unsigned int ret = 0; if (lcrd_server_conf_rdlock() != 0) { return 0; } conf = conf_get_server_conf(); if (conf == NULL) { goto out; } ret = conf->image_opt_timeout; out: (void)lcrd_server_conf_unlock(); return ret; } char *conf_get_im_server_sock_addr() { struct service_arguments *conf = NULL; char *result = NULL; if (lcrd_server_conf_rdlock() != 0) { ERROR("BUG conf_rdlock failed"); return NULL; } conf = conf_get_server_conf(); if (conf == NULL || conf->json_confs == NULL) { goto out; } result = util_strdup_s(conf->json_confs->image_server_sock_addr); out: (void)lcrd_server_conf_unlock(); return result; } bool conf_update_im_server_sock_addr(const char *new_sock_addr) { struct service_arguments *conf = NULL; bool result = true; if (new_sock_addr == NULL) { return false; } if (lcrd_server_conf_rdlock() != 0) { ERROR("BUG conf_rdlock failed"); return false; } conf = conf_get_server_conf(); if (conf == NULL || conf->json_confs == NULL) { result = false; goto out; } free(conf->json_confs->image_server_sock_addr); conf->json_confs->image_server_sock_addr = util_strdup_s(new_sock_addr); out: (void)lcrd_server_conf_unlock(); return result; } char *conf_get_enable_plugins() { struct service_arguments *conf = NULL; char *plugins = NULL; if (lcrd_server_conf_rdlock() != 0) { ERROR("BUG conf_rdlock failed"); return NULL; } conf = conf_get_server_conf(); if (conf == NULL || conf->json_confs == NULL || conf->json_confs->enable_plugins == NULL) { goto out; } plugins = util_strdup_s(conf->json_confs->enable_plugins); out: (void)lcrd_server_conf_unlock(); return plugins; } /* save args to conf */ int save_args_to_conf(struct service_arguments *args) { int ret = 0; ret = pthread_rwlock_init(&g_lcrd_conf.lcrd_conf_rwlock, NULL); if (ret != 0) { ERROR("Failed to init lcrd conf rwlock"); ret = -1; goto out; } if (pthread_rwlock_wrlock(&g_lcrd_conf.lcrd_conf_rwlock) != 0) { ERROR("Failed to acquire lcrd conf write lock"); ret = -1; goto out; } if (g_lcrd_conf.server_conf != NULL) { service_arguments_free(g_lcrd_conf.server_conf); free(g_lcrd_conf.server_conf); } g_lcrd_conf.server_conf = args; if (pthread_rwlock_unlock(&g_lcrd_conf.lcrd_conf_rwlock) != 0) { ERROR("Failed to release lcrd conf write lock"); ret = -1; goto out; } out: return ret; } /* set path group */ static int set_path_group(const char *rpath, const char *group) { struct group *grp = NULL; gid_t gid; grp = getgrnam(group); if (grp != NULL) { gid = grp->gr_gid; DEBUG("Group %s found, gid: %d", group, gid); if (chown(rpath, -1, gid) != 0) { DEBUG("Failed to chown %s to gid: %d", rpath, gid); return -1; } } else { if (strcmp(group, "docker") == 0 || strcmp(group, "lcrd") == 0) { DEBUG("Warning: could not change group %s to %s", rpath, group); } else { lcrd_set_error_message("Group %s not found", group); return -1; } } return 0; } /* set socket group */ int set_unix_socket_group(const char *socket, const char *group) { const char *path = NULL; char rpath[PATH_MAX + 1] = { 0x00 }; int ret = 0; int nret = 0; if (socket == NULL || group == NULL) { return -1; } path = socket + strlen(UNIX_SOCKET_PREFIX); if (strlen(path) > PATH_MAX || realpath(path, rpath) == NULL) { ERROR("ensure socket path %s failed", path); ret = -1; goto out; } nret = set_path_group(rpath, group); if (nret < 0) { ERROR("set group of the path: %s failed", rpath); ret = -1; goto out; } if (chmod(rpath, SOCKET_GROUP_DIRECTORY_MODE) != 0) { DEBUG("Failed to chmod for socket: %s", rpath); ret = -1; goto out; } out: if (ret == 0) { DEBUG("Listener created for HTTP on unix (%s)", rpath); } return ret; } /* maybe create cpu realtime file */ static int maybe_create_cpu_realtime_file(bool present, int64_t value, const char *file, const char *path) { int ret; int fd = 0; ssize_t nwrite; char fpath[PATH_MAX] = { 0 }; char buf[LCRD_NUMSTRLEN64] = { 0 }; if (!present || value == 0) { return 0; } ret = util_mkdir_p(path, CONFIG_DIRECTORY_MODE); if (ret != 0) { ERROR("Failed to mkdir: %s", path); return -1; } int nret = snprintf(fpath, sizeof(fpath), "%s/%s", path, file); if (nret < 0 || nret >= sizeof(fpath)) { ERROR("Failed to print string"); return -1; } nret = snprintf(buf, sizeof(buf), "%lld", (long long int)value); if (nret < 0 || (size_t)nret >= sizeof(buf)) { ERROR("Failed to print string"); return -1; } fd = util_open(fpath, O_WRONLY | O_TRUNC | O_CREAT | O_CLOEXEC, 0700); if (fd < 0) { ERROR("Failed to open file: %s: %s", fpath, strerror(errno)); lcrd_set_error_message("Failed to open file: %s: %s", fpath, strerror(errno)); return -1; } nwrite = util_write_nointr(fd, buf, strlen(buf)); if (nwrite < 0) { ERROR("Failed to write %s to %s: %s", buf, fpath, strerror(errno)); lcrd_set_error_message("Failed to write '%s' to '%s': %s", buf, fpath, strerror(errno)); close(fd); return -1; } close(fd); return 0; } static int get_cgroup_cpu_rt(int64_t *cpu_rt_period, int64_t *cpu_rt_runtime) { struct service_arguments *conf = NULL; if (lcrd_server_conf_rdlock() != 0) { return -1; } conf = conf_get_server_conf(); if (conf == NULL) { (void)lcrd_server_conf_unlock(); return -1; } *cpu_rt_period = conf->json_confs->cpu_rt_period; *cpu_rt_runtime = conf->json_confs->cpu_rt_runtime; if (lcrd_server_conf_unlock() != 0) { return -1; } return 0; } static int recursively_create_cgroup(const char *path, int recursive_depth, int64_t cpu_rt_period, int64_t cpu_rt_runtime) { int ret = 0; sysinfo_t *sysinfo = NULL; char *dup = NULL; char *dirpath = NULL; char *mnt = NULL; char *root = NULL; char fpath[PATH_MAX] = { 0 }; dup = util_strdup_s(path); dirpath = dirname(dup); ret = init_cgroups_path(dirpath, (recursive_depth + 1)); free(dup); if (ret != 0) { return ret; } ret = find_cgroup_mountpoint_and_root("cpu", &mnt, &root); if (ret != 0 || mnt == NULL || root == NULL) { ERROR("Can not find cgroup mnt and root path for subsystem 'cpu'"); lcrd_set_error_message("Can not find cgroup mnt and root path for subsystem 'cpu'"); ret = -1; goto out; } // When iSulad is run inside iSulad/docker, the root is based of the host cgroup. // Replace root to "/" if (strncmp(root, "/lxc/", strlen("/lxc/")) != 0) { root[1] = '\0'; } int nret = snprintf(fpath, sizeof(fpath), "%s/%s/%s", mnt, root, path); if (nret < 0 || (size_t)nret >= sizeof(fpath)) { ERROR("Failed to print string"); ret = -1; goto out; } sysinfo = get_sys_info(true); if (sysinfo == NULL) { ERROR("Can not get system info"); ret = -1; goto out; } ret = maybe_create_cpu_realtime_file(sysinfo->cgcpuinfo.cpu_rt_period, cpu_rt_period, "cpu.rt_period_us", fpath); if (ret != 0) { goto out; } ret = maybe_create_cpu_realtime_file(sysinfo->cgcpuinfo.cpu_rt_runtime, cpu_rt_runtime, "cpu.rt_runtime_us", fpath); if (ret != 0) { goto out; } out: free(mnt); free(root); free_sysinfo(sysinfo); return ret; } /* init cgroups path */ int init_cgroups_path(const char *path, int recursive_depth) { int64_t cpu_rt_period = 0; int64_t cpu_rt_runtime = 0; if ((recursive_depth + 1) > MAX_PATH_DEPTH) { ERROR("Reach the max cgroup depth:%s", path); return -1; } if (path == NULL || strcmp(path, "/") == 0 || strcmp(path, ".") == 0) { return 0; } if (get_cgroup_cpu_rt(&cpu_rt_period, &cpu_rt_runtime)) { return -1; } if (cpu_rt_period == 0 && cpu_rt_runtime == 0) { return 0; } // Recursively create cgroup to ensure that the system and all parent cgroups have values set // for the period and runtime as this limits what the children can be set to. if (recursively_create_cgroup(path, recursive_depth, cpu_rt_period, cpu_rt_runtime)) { return -1; } return 0; } #define OVERRIDE_STRING_VALUE(dst, src) \ do { \ if ((src) != NULL && strlen((src)) != 0) { \ free((dst)); \ (dst) = (src); \ (src) = NULL; \ } \ } while (0) static int string_array_append(char **suffix, size_t suffix_len, size_t *curr_len, char ***result) { if (suffix_len > 0) { size_t new_len = *curr_len + suffix_len; size_t work_len = *curr_len; size_t i, j; if (util_grow_array(result, &work_len, new_len, INCREMENT_INTREVAL) != 0) { return -1; } for (i = *curr_len, j = 0; i < new_len; i++) { (*result)[i] = suffix[j]; suffix[j++] = NULL; } *curr_len = new_len; } return 0; } int parse_log_opts(struct service_arguments *args, const char *key, const char *value) { int ret = -1; if (key == NULL || value == NULL) { return 0; } // support new driver options, add here if (strcmp(key, "log-path") == 0) { free(args->logpath); args->logpath = util_strdup_s(value); ret = 0; } else if (strcmp(key, "log-file-mode") == 0) { unsigned int file_mode = 0; if (util_safe_uint(value, &file_mode) == 0) { args->log_file_mode = file_mode; ret = 0; } } else if (strcmp(key, "max-file") == 0) { int tmaxfile = 0; if (util_safe_int(value, &tmaxfile) == 0 && tmaxfile > 0) { args->max_file = tmaxfile; ret = 0; } } else if (strcmp(key, "max-size") == 0) { int64_t tmaxsize = 0; if (util_parse_byte_size_string(value, &tmaxsize) == 0 && tmaxsize > 0) { args->max_size = tmaxsize; ret = 0; } } else { ERROR("Invalid config: %s = %s", key, value); } return ret; } static inline void override_string_value(char **dst, char **src) { if (*src == NULL || (*src)[0] == '\0') { return; } free(*dst); *dst = *src; *src = NULL; } static inline void override_bool_pointer_value(bool **dst, bool **src) { if (*src == NULL) { return; } free(*dst); *dst = *src; *src = NULL; } static int merge_hosts_conf_into_global(struct service_arguments *args, const isulad_daemon_configs *tmp_json_confs) { size_t i, j; for (i = 0; i < tmp_json_confs->hosts_len; i++) { for (j = 0; j < args->json_confs->hosts_len; j++) { if (strcmp(args->json_confs->hosts[j], tmp_json_confs->hosts[i]) == 0) { break; } } if (j != args->json_confs->hosts_len) { continue; } if (util_array_append(&(args->json_confs->hosts), tmp_json_confs->hosts[i]) != 0) { ERROR("merge hosts config failed"); return -1; } args->json_confs->hosts_len++; if (args->json_confs->hosts_len > MAX_HOSTS) { lcrd_set_error_message("Too many hosts, the max number is %d", MAX_HOSTS); return -1; } } return 0; } static int merge_logs_conf_into_global(struct service_arguments *args, isulad_daemon_configs *tmp_json_confs) { size_t i; override_string_value(&args->json_confs->log_level, &tmp_json_confs->log_level); override_string_value(&args->json_confs->log_driver, &tmp_json_confs->log_driver); for (i = 0; tmp_json_confs->log_opts != NULL && i < tmp_json_confs->log_opts->len; i++) { if (parse_log_opts(args, tmp_json_confs->log_opts->keys[i], tmp_json_confs->log_opts->values[i]) != 0) { COMMAND_ERROR("Failed to parse log options %s:%s", tmp_json_confs->log_opts->keys[i], tmp_json_confs->log_opts->values[i]); return -1; } if (append_json_map_string_string(args->json_confs->log_opts, tmp_json_confs->log_opts->keys[i], tmp_json_confs->log_opts->values[i]) != 0) { ERROR("Out of memory"); return -1; } } return 0; } static int merge_authorization_conf_into_global(struct service_arguments *args, isulad_daemon_configs *tmp_json_confs) { args->json_confs->tls = tmp_json_confs->tls; args->json_confs->tls_verify = tmp_json_confs->tls_verify; if (tmp_json_confs->tls_config != NULL) { override_string_value(&args->json_confs->tls_config->ca_file, &tmp_json_confs->tls_config->ca_file); override_string_value(&args->json_confs->tls_config->cert_file, &tmp_json_confs->tls_config->cert_file); override_string_value(&args->json_confs->tls_config->key_file, &tmp_json_confs->tls_config->key_file); } if (tmp_json_confs->authorization_plugin != NULL) { override_string_value(&args->json_confs->authorization_plugin, &tmp_json_confs->authorization_plugin); } return 0; } static int merge_storage_conf_into_global(struct service_arguments *args, isulad_daemon_configs *tmp_json_confs) { override_string_value(&args->json_confs->storage_driver, &tmp_json_confs->storage_driver); if (string_array_append(tmp_json_confs->storage_opts, tmp_json_confs->storage_opts_len, &(args->json_confs->storage_opts_len), &(args->json_confs->storage_opts)) != 0) { ERROR("merge graph config failed"); return -1; } return 0; } static int merge_registry_conf_into_global(struct service_arguments *args, isulad_daemon_configs *tmp_json_confs) { if (string_array_append(tmp_json_confs->registry_mirrors, tmp_json_confs->registry_mirrors_len, &(args->json_confs->registry_mirrors_len), &(args->json_confs->registry_mirrors)) != 0) { ERROR("merge registry mirrors config failed"); return -1; } if (string_array_append(tmp_json_confs->insecure_registries, tmp_json_confs->insecure_registries_len, &(args->json_confs->insecure_registries_len), &(args->json_confs->insecure_registries)) != 0) { ERROR("merge insecure registries config failed"); return -1; } return 0; } static int merge_default_ulimits_conf_into_global(struct service_arguments *args, isulad_daemon_configs *tmp_json_confs) { if (tmp_json_confs == NULL) { return -1; } if (tmp_json_confs->default_ulimits == NULL) { return 0; } args->json_confs->default_ulimits = tmp_json_confs->default_ulimits; tmp_json_confs->default_ulimits = NULL; return 0; } int merge_json_confs_into_global(struct service_arguments *args) { isulad_daemon_configs *tmp_json_confs; parser_error err = NULL; int ret = 0; tmp_json_confs = isulad_daemon_configs_parse_file(ISULAD_DAEMON_JSON_CONF_FILE, NULL, &err); if (tmp_json_confs == NULL) { COMMAND_ERROR("Load isulad json config failed: %s", err != NULL ? err : ""); ret = -1; goto out; } // Daemon socket option if (merge_hosts_conf_into_global(args, tmp_json_confs)) { ret = -1; goto out; } override_string_value(&args->json_confs->group, &tmp_json_confs->group); override_string_value(&args->json_confs->graph, &tmp_json_confs->graph); override_string_value(&args->json_confs->state, &tmp_json_confs->state); if (merge_logs_conf_into_global(args, tmp_json_confs)) { ret = -1; goto out; } override_string_value(&args->json_confs->pidfile, &tmp_json_confs->pidfile); // iSulad runtime execution options override_string_value(&args->json_confs->engine, &tmp_json_confs->engine); override_string_value(&args->json_confs->hook_spec, &tmp_json_confs->hook_spec); override_string_value(&args->json_confs->enable_plugins, &tmp_json_confs->enable_plugins); override_string_value(&args->json_confs->native_umask, &tmp_json_confs->native_umask); override_string_value(&args->json_confs->cgroup_parent, &tmp_json_confs->cgroup_parent); override_string_value(&args->json_confs->rootfsmntdir, &tmp_json_confs->rootfsmntdir); override_string_value(&args->json_confs->start_timeout, &tmp_json_confs->start_timeout); override_string_value(&args->json_confs->image_opt_timeout, &tmp_json_confs->image_opt_timeout); override_string_value(&args->json_confs->image_server_sock_addr, &tmp_json_confs->image_server_sock_addr); override_string_value(&args->json_confs->pod_sandbox_image, &tmp_json_confs->pod_sandbox_image); override_string_value(&args->json_confs->network_plugin, &tmp_json_confs->network_plugin); override_string_value(&args->json_confs->cni_bin_dir, &tmp_json_confs->cni_bin_dir); override_string_value(&args->json_confs->cni_conf_dir, &tmp_json_confs->cni_conf_dir); // Daemon storage-driver if (merge_storage_conf_into_global(args, tmp_json_confs)) { ret = -1; goto out; } if (merge_registry_conf_into_global(args, tmp_json_confs)) { ret = -1; goto out; } if (tmp_json_confs->cpu_rt_period > 0) { args->json_confs->cpu_rt_period = tmp_json_confs->cpu_rt_period; } if (tmp_json_confs->cpu_rt_runtime > 0) { args->json_confs->cpu_rt_runtime = tmp_json_confs->cpu_rt_runtime; } if (tmp_json_confs->image_service) { args->json_confs->image_service = tmp_json_confs->image_service; } if (tmp_json_confs->image_layer_check) { args->json_confs->image_layer_check = tmp_json_confs->image_layer_check; } override_bool_pointer_value(&args->json_confs->use_decrypted_key, &tmp_json_confs->use_decrypted_key); if (tmp_json_confs->insecure_skip_verify_enforce) { args->json_confs->insecure_skip_verify_enforce = tmp_json_confs->insecure_skip_verify_enforce; } if (merge_authorization_conf_into_global(args, tmp_json_confs)) { ret = -1; goto out; } if (merge_default_ulimits_conf_into_global(args, tmp_json_confs)) { ret = -1; goto out; } out: free(err); free_isulad_daemon_configs(tmp_json_confs); return ret; }