/****************************************************************************** * Copyright (c) Huawei Technologies Co., Ltd. 2017-2019. All rights reserved. * iSulad licensed under the Mulan PSL v2. * You can use this software according to the terms and conditions of the Mulan PSL v2. * You may obtain a copy of Mulan PSL v2 at: * http://license.coscl.org.cn/MulanPSL2 * 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 v2 for more details. * Author: lifeng * Create: 2017-11-22 * Description: provide init process of isulad ******************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef SYSTEMD_NOTIFY #include #endif #include "constants.h" #include "libisulad.h" #include "collector.h" #include "commands.h" #include "log.h" #include "engine.h" #include "utils.h" #include "isulad_config.h" #include "image.h" #include "sysinfo.h" #include "verify.h" #include "monitord.h" #include "service_common.h" #include "callback.h" #include "log_gather.h" #include "containers_store.h" #include "restore.h" #include "supervisor.h" #include "containers_gc.h" #include "plugin.h" #include "selinux_label.h" #ifdef ENABLE_OCI_IMAGE #include "driver.h" #endif #ifdef GRPC_CONNECTOR #include "clibcni/api.h" #endif #ifdef ENABLE_EMBEDDED_IMAGE #include "db_common.h" #endif sem_t g_daemon_shutdown_sem; static int create_client_run_path(const char *group) { int ret = 0; const char *rundir = "/var/run/isula"; if (group == NULL) { return -1; } ret = util_mkdir_p(rundir, DEFAULT_SECURE_DIRECTORY_MODE); if (ret < 0) { ERROR("Unable to create client run directory %s.", rundir); return ret; } ret = chmod(rundir, DEFAULT_SECURE_DIRECTORY_MODE); if (ret < 0) { ERROR("Failed to chmod for client run path: %s", rundir); } return ret; } static int mount_rootfs_mnt_dir(const char *mountdir) { int ret = -1; char *p = NULL; char *rootfsdir = NULL; mountinfo_t **minfos = NULL; mountinfo_t *info = NULL; if (mountdir == NULL) { ERROR("parent mount path is NULL"); goto out; } rootfsdir = util_strdup_s(mountdir); ret = util_mkdir_p(rootfsdir, ROOTFS_MNT_DIRECTORY_MODE); if (ret < 0) { ERROR("Failed to create rootfs directory:%s", rootfsdir); goto out; } // find parent directory p = strrchr(rootfsdir, '/'); if (p == NULL) { ERROR("Failed to find parent directory for %s", rootfsdir); goto out; } *p = '\0'; minfos = getmountsinfo(); if (minfos == NULL) { ERROR("Failed to get mounts info"); goto out; } info = find_mount_info(minfos, rootfsdir); if (info == NULL) { ret = mount(rootfsdir, rootfsdir, "bind", MS_BIND | MS_REC, NULL); if (ret < 0) { ERROR("Failed to mount parent directory %s:%s", rootfsdir, strerror(errno)); goto out; } } ret = 0; out: free(rootfsdir); free_mounts_info(minfos); return ret; } #ifdef ENABLE_OCI_IMAGE static int umount_rootfs_mnt_dir(const char *mntdir) { int ret = -1; char *p = NULL; char *dir = NULL; dir = util_strdup_s(mntdir); // find parent directory p = strrchr(dir, '/'); if (p == NULL) { ERROR("Failed to find parent directory for %s", dir); goto out; } *p = '\0'; ret = umount(dir); if (ret < 0 && errno != EINVAL) { WARN("Failed to umount parent directory %s:%s", dir, strerror(errno)); goto out; } ret = 0; out: free(dir); return ret; } static void umount_daemon_mntpoint() { char *mntdir = NULL; mntdir = conf_get_isulad_mount_rootfs(); if (mntdir == NULL) { ERROR("Out of memory"); } else { umount_rootfs_mnt_dir(mntdir); free(mntdir); mntdir = NULL; } graphdriver_umount_mntpoint(); } #endif static inline bool unlink_ignore_enoent(const char *fname) { return unlink(fname) && errno != ENOENT; } static void clean_residual_files() { char *checked_flag = NULL; char *fname = NULL; /* remove image checked file */ checked_flag = conf_get_graph_check_flag_file(); if (checked_flag == NULL) { ERROR("Failed to get image checked flag file path"); } else if (unlink_ignore_enoent(checked_flag)) { ERROR("Unlink file: %s error: %s", checked_flag, strerror(errno)); } free(checked_flag); /* remove pid file */ fname = conf_get_isulad_pidfile(); if (fname == NULL) { ERROR("Failed to get isulad pid file path"); } else if (unlink(fname) && errno != ENOENT) { WARN("Unlink file: %s error: %s", fname, strerror(errno)); } free(fname); #ifdef ENABLE_OCI_IMAGE /* remove image server socket file */ fname = conf_get_im_server_sock_addr(); if (fname != NULL && unlink_ignore_enoent(fname + strlen(UNIX_SOCKET_PREFIX))) { WARN("Unlink file: %s error: %s", fname + strlen(UNIX_SOCKET_PREFIX), strerror(errno)); } free(fname); #define ISULAD_KIT_PID_FILE "/var/run/isula_image.pid" #define ISULAD_KIT_INFO_FILE "/var/run/isula_image.info" if (unlink_ignore_enoent(ISULAD_KIT_PID_FILE)) { WARN("Unlink file: %s error: %s", ISULAD_KIT_PID_FILE, strerror(errno)); } if (unlink_ignore_enoent(ISULAD_KIT_INFO_FILE)) { WARN("Unlink file: %s error: %s", ISULAD_KIT_INFO_FILE, strerror(errno)); } #endif } static void daemon_shutdown() { /* clean resource first, left time to wait finish */ image_module_exit(); #ifdef ENABLE_EMBEDDED_IMAGE /* shutdown db */ db_common_finish(); #endif /* shutdown server */ server_common_shutdown(); #ifdef ENABLE_OCI_IMAGE umount_daemon_mntpoint(); #endif clean_residual_files(); } static void sigint_handler(int x) { INFO("Got SIGINT; exiting"); sem_post(&g_daemon_shutdown_sem); } static void sigterm_handler(int signo) { INFO("Got SIGTERM; exiting"); sem_post(&g_daemon_shutdown_sem); } static int ignore_signals() { struct sigaction sa; /* * Ignore SIGHUP so isulad process still exists after * terminal die. */ (void)memset(&sa, 0, sizeof(struct sigaction)); sa.sa_handler = SIG_IGN; sigemptyset(&sa.sa_mask); sa.sa_flags = 0; if (sigaction(SIGHUP, &sa, NULL) < 0) { ERROR("Failed to ignore SIGHUP"); return -1; } if (sigaction(SIGPIPE, &sa, NULL) < 0) { ERROR("Failed to ignore SIGPIPE"); return -1; } if (sigaction(SIGUSR1, &sa, NULL) < 0) { ERROR("Failed to ignore SIGUSR1"); return -1; } return 0; } static int add_shutdown_signal_handler() { struct sigaction sa; (void)memset(&sa, 0, sizeof(struct sigaction)); if (sem_init(&g_daemon_shutdown_sem, 0, 0) == -1) { ERROR("Failed to init daemon shutdown sem"); return -1; } sa.sa_handler = sigint_handler; sigemptyset(&sa.sa_mask); sa.sa_flags = 0; if (sigaction(SIGINT, &sa, NULL) < 0) { ERROR("Failed to add handler for SIGINT"); return -1; } (void)memset(&sa, 0, sizeof(struct sigaction)); sa.sa_handler = sigterm_handler; sigemptyset(&sa.sa_mask); sa.sa_flags = 0; if (sigaction(SIGTERM, &sa, NULL) < 0) { ERROR("Failed to add handler for SIGTERM"); return -1; } return 0; } static int add_sighandler() { if (ignore_signals() != 0) { ERROR("Failed to ignore signals"); return -1; } if (add_shutdown_signal_handler() != 0) { ERROR("Failed to add shutdown signals"); return -1; } return 0; } static int daemonize() { int ret = 0; struct service_arguments *args = NULL; umask(0000); if (isulad_server_conf_rdlock()) { ret = -1; goto out; } args = conf_get_server_conf(); if (args == NULL) { ERROR("Failed to get isulad server config"); ret = -1; goto unlock_out; } if (args->json_confs != NULL && create_client_run_path(args->json_confs->group)) { ERROR("Create client run directory failed"); ret = -1; goto unlock_out; } /* * close all file descriptors */ if (util_check_inherited(true, -1)) { ERROR("Failed to close fds."); ret = -1; } unlock_out: if (isulad_server_conf_unlock()) { ret = -1; } out: umask(0022); return ret; } int check_and_save_pid(const char *fn) { int fd = -1; int ret = 0; int len = 0; struct flock lk; char pidbuf[ISULAD_NUMSTRLEN64] = { 0 }; if (fn == NULL) { ERROR("Input error"); return -1; } ret = util_build_dir(fn); if (ret) { WARN("Failed to create directory for pid file: %s", fn); return -1; } fd = util_open(fn, O_RDWR | O_CREAT, DEFAULT_SECURE_FILE_MODE); if (fd < 0) { WARN("Failed to open pid file: %s", fn); return -1; } lk.l_type = F_WRLCK; lk.l_whence = SEEK_SET; lk.l_start = 0; lk.l_len = 0; if (fcntl(fd, F_SETLK, &lk) != 0) { /* another daemonize instance is already running, don't start up */ COMMAND_ERROR("Pid file found, ensure isulad is not running or delete pid file %s" " or please specify another pid file", fn); ret = -1; goto out; } ret = ftruncate(fd, 0); if (ret != 0) { ERROR("Failed to truncate pid file:%s to 0: %s", fn, strerror(errno)); ret = -1; goto out; } len = snprintf(pidbuf, sizeof(pidbuf), "%lu\n", (unsigned long)getpid()); if (len < 0 || len >= sizeof(pidbuf)) { ERROR("failed sprint pidbuf"); ret = -1; goto out; } len = (int)write(fd, pidbuf, strlen(pidbuf)); if (len < 0) { ERROR("Failed to write pid to file:%s: %s", fn, strerror(errno)); ret = -1; } out: if (ret < 0) { close(fd); } return ret; } int check_and_set_default_isulad_log_file(struct service_arguments *args) { if (args == NULL) { return -1; } if (args != NULL && args->json_confs != NULL && args->json_confs->log_driver != NULL && strcasecmp("file", args->json_confs->log_driver) == 0) { if ((args->logpath == NULL || strcmp("", args->logpath) == 0) && args->json_confs->graph != NULL) { free(args->logpath); args->logpath = util_strdup_s(args->json_confs->graph); } } if (args != NULL && util_validate_absolute_path(args->logpath)) { ERROR("Daemon log path \"%s\" must be abosulte path.", args->logpath); return -1; } return 0; } static int set_parent_mount_dir(struct service_arguments *args) { int ret = -1; int nret; size_t len; char *rootfsdir = NULL; if (args->json_confs == NULL) { ERROR("Empty json configs"); goto out; } if (strlen(args->json_confs->graph) > (SIZE_MAX - strlen("/mnt/rootfs")) - 1) { ERROR("Root directory of the isulad runtime is too long"); goto out; } len = strlen(args->json_confs->graph) + strlen("/mnt/rootfs") + 1; if (len > PATH_MAX) { ERROR("The size of path exceeds the limit"); goto out; } rootfsdir = util_common_calloc_s(len); if (rootfsdir == NULL) { ERROR("Out of memory"); goto out; } nret = snprintf(rootfsdir, len, "%s/mnt/rootfs", args->json_confs->graph); if (nret < 0 || (size_t)nret >= len) { ERROR("Failed to print string"); goto out; } free(args->json_confs->rootfsmntdir); args->json_confs->rootfsmntdir = util_strdup_s(rootfsdir); ret = 0; out: free(rootfsdir); return ret; } static int check_hook_spec_file(const char *hook_spec) { struct stat hookstat = { 0 }; if (hook_spec == NULL) { return 0; } if (util_validate_absolute_path(hook_spec)) { ERROR("Hook path \"%s\" must be an absolute path", hook_spec); return -1; } if (stat(hook_spec, &hookstat)) { ERROR("Stat hook spec file failed: %s", strerror(errno)); return -1; } if ((hookstat.st_mode & S_IFMT) != S_IFREG) { ERROR("Hook spec file must be a regular text file"); return -1; } return 0; } static int parse_hook_spec(const char *specfile, oci_runtime_spec_hooks **phooks) { int ret = 0; parser_error err = NULL; oci_runtime_spec_hooks *hooks = NULL; if (check_hook_spec_file(specfile)) { ret = -1; goto out; } hooks = oci_runtime_spec_hooks_parse_file(specfile, NULL, &err); if (hooks == NULL) { ERROR("Failed to parse hook-spec file: %s", err); isulad_set_error_message("Invalid hook-spec file(%s) in server conf.", specfile); ret = -1; goto out; } ret = verify_oci_hook(hooks); if (ret) { ERROR("Verify hook file failed"); free_oci_runtime_spec_hooks(hooks); goto out; } *phooks = hooks; out: free(err); err = NULL; return ret; } static void update_isulad_rlimits() { #define __ULIMIT_CONFIG_VAL_ 1048576 struct rlimit limit; /* set ulimit of process */ limit.rlim_cur = __ULIMIT_CONFIG_VAL_; limit.rlim_max = __ULIMIT_CONFIG_VAL_; if (setrlimit(RLIMIT_NOFILE, &limit)) { WARN("Can not set ulimit of RLIMIT_NOFILE: %s", strerror(errno)); } if (setrlimit(RLIMIT_NPROC, &limit)) { WARN("Can not set ulimit of RLIMIT_NPROC: %s", strerror(errno)); } limit.rlim_cur = RLIM_INFINITY; limit.rlim_max = RLIM_INFINITY; if (setrlimit(RLIMIT_CORE, &limit)) { WARN("Can not set ulimit of RLIMIT_CORE: %s", strerror(errno)); } } static int validate_time_duration(const char *value) { regex_t preg; int status = 0; regmatch_t regmatch = { 0 }; if (value == NULL) { return -1; } if (regcomp(&preg, "^([1-9][0-9]*)+([s,m])$", REG_NOSUB | REG_EXTENDED)) { ERROR("Failed to compile the regex\n"); return -1; } status = regexec(&preg, value, 1, ®match, 0); regfree(&preg); if (status != 0) { ERROR("Error start-timeout value: %s\n", value); COMMAND_ERROR("Invalid time duration value(%s) in server conf, " "only ^([1-9][0-9]*)+([s,m])$ are allowed.", value); return -1; } return 0; } static int parse_time_duration(const char *value, unsigned int *seconds) { int ret = 0; unsigned int tmp = 0; char unit = 0; size_t len = 0; char *num_str = NULL; if (validate_time_duration(value) != 0) { return -1; } num_str = util_strdup_s(value); len = strlen(value); unit = *(value + len - 1); *(num_str + len - 1) = '\0'; ret = util_safe_uint(num_str, &tmp); if (ret < 0) { ERROR("Illegal unsigned integer: %s", num_str); COMMAND_ERROR("Illegal unsigned integer:%s:%s", num_str, strerror(-ret)); ret = -1; goto out; } if (tmp == 0) { goto out; } switch (unit) { case 'm': if (UINT_MAX / tmp > 60) { tmp *= 60; } else { ERROR("The time duration value(%s) is too large, please reset it!", num_str); COMMAND_ERROR("The time duration value(%s) is too large, please reset it!", num_str); ret = -1; } break; case 's': break; default: COMMAND_ERROR("Unsupported unit:%c", unit); ret = -1; break; } *seconds = tmp; out: free(num_str); return ret; } // update values for options after flag parsing is complete static int update_tls_options(struct service_arguments *args) { int ret = 0; char *ca_real_file = NULL; char *cert_real_file = NULL; char *key_real_file = NULL; if (args->json_confs->tls_verify) { args->json_confs->tls = true; } if (!args->json_confs->tls) { free_isulad_daemon_configs_tls_config(args->json_confs->tls_config); args->json_confs->tls_config = NULL; } else { if (args->json_confs->tls_verify) { ca_real_file = verify_file_and_get_real_path(args->json_confs->tls_config->ca_file); if (ca_real_file == NULL) { ERROR("Invalid CaFile(%s)!", args->json_confs->tls_config->ca_file); COMMAND_ERROR("Invalid CaFile(%s)", args->json_confs->tls_config->ca_file); ret = -1; goto out; } free(args->json_confs->tls_config->ca_file); args->json_confs->tls_config->ca_file = ca_real_file; } cert_real_file = verify_file_and_get_real_path(args->json_confs->tls_config->cert_file); if (cert_real_file == NULL) { ERROR("Invalid CertFile(%s)", args->json_confs->tls_config->cert_file); COMMAND_ERROR("Invalid CertFile(%s)", args->json_confs->tls_config->cert_file); ret = -1; goto out; } free(args->json_confs->tls_config->cert_file); args->json_confs->tls_config->cert_file = cert_real_file; key_real_file = verify_file_and_get_real_path(args->json_confs->tls_config->key_file); if (key_real_file == NULL) { ERROR("Invalid KeyFile(%s)", args->json_confs->tls_config->key_file); COMMAND_ERROR("Invalid CertFile(%s)", args->json_confs->tls_config->key_file); ret = -1; goto out; } free(args->json_confs->tls_config->key_file); args->json_confs->tls_config->key_file = key_real_file; } out: return ret; } static int update_set_default_log_file(struct service_arguments *args) { int ret = 0; if (args->json_confs->log_driver && strcasecmp("stdout", args->json_confs->log_driver) == 0) { args->quiet = false; } if (check_and_set_default_isulad_log_file(args)) { ret = -1; goto out; } out: return ret; } static int parse_conf_hooks(struct service_arguments *args) { int ret = 0; if (args->json_confs->hook_spec != NULL && parse_hook_spec(args->json_confs->hook_spec, &args->hooks) != 0) { ret = -1; goto out; } out: return ret; } static int parse_conf_time_duration(struct service_arguments *args) { int ret = 0; /* parse start timeout */ if (args->json_confs->start_timeout != NULL && parse_time_duration(args->json_confs->start_timeout, &args->start_timeout)) { ret = -1; goto out; } /* parse image opt timeout */ if (args->json_confs->image_opt_timeout != NULL && parse_time_duration(args->json_confs->image_opt_timeout, &args->image_opt_timeout)) { ret = -1; goto out; } out: return ret; } static int overlay_supports_selinux(bool *supported) { #define KALLSYMS_ITEM_MAX_LEN 100 int ret = 0; FILE *fp = NULL; char *buf = NULL; size_t len; ssize_t num; *supported = false; fp = fopen("/proc/kallsyms", "re"); if (fp == NULL) { ERROR("Failed to open /proc/kallsyms: %s", strerror(errno)); return -1; } __fsetlocking(fp, FSETLOCKING_BYCALLER); for (num = getline(&buf, &len, fp); num != -1; num = getline(&buf, &len, fp)) { char sym_addr[KALLSYMS_ITEM_MAX_LEN] = { 0 }; char sym_type[KALLSYMS_ITEM_MAX_LEN] = { 0 }; char sym_name[KALLSYMS_ITEM_MAX_LEN] = { 0 }; if (sscanf(buf, "%s %s %s", sym_addr, sym_type, sym_name) != 3) { ERROR("sscanf buffer failed"); ret = -1; goto out; } // Check for presence of symbol security_inode_copy_up. if (strcmp(sym_name, "security_inode_copy_up") == 0) { *supported = true; goto out; } } out: free(buf); fclose(fp); return ret; } static int configure_kernel_security_support(const struct service_arguments *args) { if (selinux_state_init() != 0) { ERROR("Failed to init selinux state"); return -1; } if (args->json_confs->selinux_enabled) { if (!selinux_get_enable()) { WARN("iSulad could not enable SELinux on the host system"); return 0; } if (strcmp(args->json_confs->storage_driver, "overlay") == 0 || strcmp(args->json_confs->storage_driver, "overlay2") == 0) { // If driver is overlay or overlay2, make sure kernel // supports selinux with overlay. bool supported = false; if (overlay_supports_selinux(&supported)) { return -1; } if (!supported) { WARN("SELinux is not supported with the %s graph driver on this kernel", args->json_confs->storage_driver); } } } else { selinux_set_disabled(); } return 0; } static int update_server_args(struct service_arguments *args) { int ret = 0; if (update_tls_options(args)) { ret = -1; goto out; } if (update_set_default_log_file(args) != 0) { ret = -1; goto out; } if (update_hosts(args) != 0) { ret = -1; goto out; } if (update_default_ulimit(args) != 0) { ret = -1; goto out; } /* check args */ if (check_args(args)) { ret = -1; goto out; } if (set_parent_mount_dir(args)) { ret = -1; goto out; } /* parse hook spec */ if (parse_conf_hooks(args) != 0) { ret = -1; goto out; } /* parse image opt timeout */ if (parse_conf_time_duration(args) != 0) { ret = -1; goto out; } // Configure and validate the kernels security support. Note this is a Linux/FreeBSD // operation only, so it is safe to pass *just* the runtime OS graphdriver. if (configure_kernel_security_support(args)) { ret = -1; goto out; } #ifdef ENABLE_OCI_IMAGE args->driver = graphdriver_init(args->json_confs->storage_driver, args->json_confs->storage_opts, args->json_confs->storage_opts_len); if (args->driver == NULL) { ret = -1; goto out; } #endif out: return ret; } static int server_conf_parse_save(int argc, const char **argv) { int ret = 0; struct service_arguments *args = NULL; args = util_common_calloc_s(sizeof(struct service_arguments)); if (args == NULL) { ERROR("memory out"); ret = -1; goto out; } /* Step1: set default value to configs */ if (service_arguments_init(args) != 0) { ERROR("Failed to init service arguments"); ret = -1; goto out; } /* Step2: load json configs and merge into global configs */ if (merge_json_confs_into_global(args) != 0) { ret = -1; goto out; } /* Step3: option from command line override configuration file */ if (parse_args(args, argc, argv)) { ERROR("parse args failed"); ret = -1; goto out; } if (update_server_args(args) != 0) { ret = -1; goto out; } if (save_args_to_conf(args)) { ERROR("Failed to save arguments"); ret = -1; goto out; } out: if (ret != 0) { service_arguments_free(args); free(args); } return ret; } static int init_log_gather_thread(const char *log_full_path, struct log_config *plconf, const struct service_arguments *args) { pthread_t log_thread = { 0 }; struct log_gather_conf lgconf = { 0 }; int log_gather_exitcode = -1; lgconf.log_file_mode = args->log_file_mode; lgconf.fifo_path = plconf->file; lgconf.g_log_driver = plconf->driver; lgconf.log_path = log_full_path; lgconf.max_size = args->max_size; lgconf.max_file = args->max_file; lgconf.exitcode = &log_gather_exitcode; if (pthread_create(&log_thread, NULL, log_gather, &lgconf)) { printf("Failed to create log monitor thread\n"); return -1; } while (1) { usleep_nointerupt(1000); if (log_gather_exitcode >= 0) { break; } } return log_gather_exitcode; } static int isulad_get_log_path(char **log_full_path, char **fifo_full_path) { int ret = 0; *log_full_path = conf_get_isulad_log_file(); if (*log_full_path == NULL) { ret = -1; goto out; } *fifo_full_path = conf_get_isulad_log_gather_fifo_path(); if (*fifo_full_path == NULL) { ret = -1; goto out; } out: return ret; } static int isulad_server_init_log(const struct service_arguments *args, const char *log_full_path, const char *fifo_full_path) { #define FIFO_DRIVER "fifo" int ret = -1; struct log_config lconf = { 0 }; lconf.name = args->progname; lconf.file = fifo_full_path; lconf.driver = FIFO_DRIVER; lconf.priority = args->json_confs->log_level; if (log_init(&lconf) != 0) { ERROR("Failed to init log"); goto out; } #ifdef GRPC_CONNECTOR /* init clibcni log */ if (cni_log_init(FIFO_DRIVER, fifo_full_path, args->json_confs->log_level) != 0) { ERROR("Failed to init cni log"); goto out; } #endif lconf.driver = args->json_confs->log_driver; if (init_log_gather_thread(log_full_path, &lconf, args)) { ERROR("Log gather start failed"); goto out; } ret = 0; out: return ret; } static int isulad_server_pre_init(const struct service_arguments *args, const char *log_full_path, const char *fifo_full_path) { int ret = 0; if (check_and_save_pid(args->json_confs->pidfile) != 0) { ERROR("Failed to save pid"); ret = -1; goto out; } if (isulad_server_init_log(args, log_full_path, fifo_full_path) != 0) { ret = -1; goto out; } if (util_mkdir_p(args->json_confs->state, DEFAULT_SECURE_FILE_MODE) != 0) { ERROR("Unable to create state directory %s.", args->json_confs->state); ret = -1; goto out; } if (util_mkdir_p(args->json_confs->graph, CONFIG_DIRECTORY_MODE) != 0) { ERROR("Unable to create root directory %s.", args->json_confs->graph); ret = -1; goto out; } if (mount_rootfs_mnt_dir(args->json_confs->rootfsmntdir)) { ERROR("Create and mount parent directory failed"); ret = -1; goto out; } #ifdef ENABLE_EMBEDDED_IMAGE if (db_common_init(args->json_confs->graph)) { ERROR("Failed to init database"); ret = -1; goto out; } #endif if (service_callback_init()) { ERROR("Failed to init service callback"); ret = -1; goto out; } out: return ret; } static int isulad_server_init_common() { int ret = -1; struct service_arguments *args = NULL; char *log_full_path = NULL; char *fifo_full_path = NULL; if (isulad_get_log_path(&log_full_path, &fifo_full_path) != 0) { goto out; } if (isulad_server_conf_rdlock()) { goto out; } args = conf_get_server_conf(); if (args == NULL) { ERROR("Failed to get isulad server config"); goto unlock_out; } if (isulad_server_pre_init(args, log_full_path, fifo_full_path) != 0) { goto unlock_out; } if (image_module_init(args->json_confs->graph)) { ERROR("Failed to init image manager"); goto unlock_out; } #ifdef ENABLE_OCI_IMAGE /* update status of graphdriver after image server running */ update_graphdriver_status(&(args->driver)); #endif if (containers_store_init()) { ERROR("Failed to init containers store"); goto unlock_out; } if (name_index_init()) { ERROR("Failed to init name index"); goto unlock_out; } ret = 0; unlock_out: if (isulad_server_conf_unlock()) { ret = -1; goto out; } out: free(log_full_path); free(fifo_full_path); return ret; } static char *parse_host(bool tls, const char *val) { char *host = NULL; char *tmp = util_strdup_s(val); tmp = util_trim_space(tmp); if (tmp == NULL) { if (tls) { host = util_strdup_s(DEFAULT_TLS_HOST); } else { host = util_strdup_s(DEFAULT_UNIX_SOCKET); } } else { host = util_strdup_s(val); } free(tmp); return host; } static int listener_init(const char *proto, const char *addr, const char *socket_group) { int ret = 0; if (proto == NULL || addr == NULL) { FATAL("Invalid input arguments"); return -1; } if (strcmp(proto, "unix") == 0) { ret = set_unix_socket_group(addr, socket_group); if (ret) { FATAL("Can't create unix socket %s", addr); ret = -1; goto out; } } out: return ret; } static int load_listener(const struct service_arguments *args) { int ret = 0; const char *delim = "://"; char *proto = NULL; char *addr = NULL; size_t i; for (i = 0; i < args->hosts_len; i++) { char *proto_addr = NULL; proto_addr = parse_host(args->json_confs->tls, args->hosts[i]); proto = strtok_r(proto_addr, delim, &addr); if (proto == NULL) { ERROR("Failed to get proto"); ret = -1; free(proto_addr); goto out; } addr += strlen("://") - 1; if (strncmp(proto, "tcp", strlen("tcp")) == 0 && (args->json_confs->tls_config == NULL || !args->json_confs->tls_verify)) { WARN("[!] DON'T BIND ON ANY IP ADDRESS WITHOUT setting" " --tlsverify IF YOU DON'T KNOW WHAT YOU'RE DOING [!]"); } // note: If we're binding to a TCP port, make sure that a container doesn't try to use it. ret = listener_init(proto, args->hosts[i], args->json_confs->group); free(proto_addr); if (ret != 0) { ERROR("Failed to init listener"); ret = -1; goto out; } } out: return ret; } static int isulad_server_init_service() { int ret = -1; struct service_arguments *args = NULL; if (isulad_server_conf_rdlock()) { goto out; } args = conf_get_server_conf(); if (args == NULL) { ERROR("Failed to get isulad server config"); goto unlock_out; } #ifdef GRPC_CONNECTOR INFO("Creating grpc server..."); #else INFO("Creating rest server..."); #endif if (server_common_init(args)) { ERROR("Failed to init service"); goto unlock_out; } ret = load_listener(args); if (ret != 0) { ERROR("Failed to load listener"); goto unlock_out; } unlock_out: if (isulad_server_conf_unlock()) { ret = -1; goto out; } out: return ret; } static int isulad_server_init_engines() { int ret = 0; char *engine = NULL; engine = conf_get_isulad_engine(); if (engine == NULL) { ret = -1; goto out; } if (engines_global_init()) { ERROR("Init engines global failed"); ret = -1; goto out; } /* Init default engine, now is lcr */ if (engines_discovery(engine)) { ERROR("Failed to discovery default engine:%s", engine); ret = -1; } out: free(engine); return ret; } static void set_mallopt() { if (mallopt(M_ARENA_TEST, 8) == 0) { fprintf(stderr, "change M_ARENA_TEST to 8\n"); } if (mallopt(M_TOP_PAD, 32 * 1024) == 0) { fprintf(stderr, "chagne M_TOP_PAD to 32KB"); } if (mallopt(M_TRIM_THRESHOLD, 64 * 1024) == 0) { fprintf(stderr, "change M_TRIM_THRESHOLD to 64KB"); } if (mallopt(M_MMAP_THRESHOLD, 64 * 1024) == 0) { fprintf(stderr, "change M_MMAP_THRESHOLD to 64KB"); } } static int start_monitord() { int ret = 0; int monitord_exitcode = 0; sem_t monitord_sem; struct monitord_sync_data msync = { 0 }; msync.monitord_sem = &monitord_sem; msync.exit_code = &monitord_exitcode; if (sem_init(msync.monitord_sem, 0, 0)) { isulad_set_error_message("Failed to init monitor sem"); ret = -1; goto out; } if (new_monitord(&msync)) { isulad_set_error_message("Create monitord thread failed"); ret = -1; sem_destroy(msync.monitord_sem); goto out; } sem_wait(msync.monitord_sem); sem_destroy(msync.monitord_sem); if (monitord_exitcode) { isulad_set_error_message("Monitord start failed"); ret = -1; goto out; } out: return ret; } /* shutdown handler */ static void *do_shutdown_handler(void *arg) { int res = 0; res = pthread_detach(pthread_self()); if (res != 0) { CRIT("Set thread detach fail"); } prctl(PR_SET_NAME, "Shutdown"); sem_wait(&g_daemon_shutdown_sem); daemon_shutdown(); exit(0); } /* news_shutdown_handler */ int new_shutdown_handler() { int ret = -1; pthread_t shutdown_thread; INFO("Starting new shutdown handler..."); ret = pthread_create(&shutdown_thread, NULL, do_shutdown_handler, NULL); if (ret != 0) { CRIT("Thread creation failed"); goto out; } ret = 0; out: return ret; } static int start_daemon_threads(char **msg) { int ret = -1; if (new_shutdown_handler()) { *msg = "Create new shutdown handler thread failed"; goto out; } if (newcollector()) { *msg = "Create collector thread failed"; goto out; } if (start_monitord()) { *msg = g_isulad_errmsg ? g_isulad_errmsg : "Failed to init cgroups path"; goto out; } if (new_gchandler()) { *msg = "Create garbage handler thread failed"; goto out; } if (new_supervisor()) { *msg = "Create supervisor thread failed"; goto out; } containers_restore(); /* sync containers list with remote */ im_sync_containers_isuladkit(); if (start_gchandler()) { *msg = "Failed to start garbage collecotor handler"; goto out; } ret = 0; out: return ret; } static int pre_init_daemon_log() { struct log_config lconf = { 0 }; lconf.name = "isulad"; lconf.quiet = true; lconf.file = NULL; lconf.priority = "ERROR"; lconf.driver = "stdout"; if (log_init(&lconf)) { fprintf(stderr, "log init failed\n"); return -1; } return 0; } static int pre_init_daemon(int argc, char **argv, char **msg) { int ret = -1; /* * must call isulad by root */ if (geteuid() != 0) { *msg = "iSulad must be called by root"; goto out; } if (server_conf_parse_save(argc, (const char **)argv)) { *msg = g_isulad_errmsg ? g_isulad_errmsg : "Failed to parse and save server conf"; goto out; } /* note: daemonize will close all fds */ if (daemonize()) { *msg = "Failed to become a daemon"; goto out; } if (isulad_server_init_engines()) { *msg = "Failed to init engines"; goto out; } /* * change the current working dir to root. */ if (chdir("/") < 0) { *msg = "Failed to change dir to /"; goto out; } ret = 0; out: return ret; } /* * Takes socket path as argument */ int main(int argc, char **argv) { struct timespec t_start, t_end; double use_time = 0; char *msg = NULL; prctl(PR_SET_NAME, "isulad"); if (pre_init_daemon_log() != 0) { exit(ECOMMON); } set_mallopt(); update_isulad_rlimits(); clock_gettime(CLOCK_MONOTONIC, &t_start); if (pre_init_daemon(argc, argv, &msg) != 0) { goto failure; } if (isulad_server_init_common() != 0) { goto failure; } if (init_cgroups_path("/lxc", 0)) { msg = g_isulad_errmsg ? g_isulad_errmsg : "Failed to init cgroups path"; goto failure; } if (add_sighandler()) { msg = "Failed to add sig handlers"; goto failure; } if (start_daemon_threads(&msg)) { goto failure; } if (isulad_server_init_service()) { msg = "Failed to init services"; goto failure; } if (start_plugin_manager()) { msg = "Failed to init plugin_manager"; goto failure; } clock_gettime(CLOCK_MONOTONIC, &t_end); use_time = (double)(t_end.tv_sec - t_start.tv_sec) * (double)1000000000 + (double)(t_end.tv_nsec - t_start.tv_nsec); use_time /= 1000000000; INFO("iSulad successfully booted in %.3f s", use_time); #ifdef GRPC_CONNECTOR INFO("Starting grpc server..."); #else INFO("Starting rest server..."); #endif #ifdef SYSTEMD_NOTIFY if (sd_notify(0, "READY=1") < 0) { msg = "Failed to send notify the service manager about state changes"; goto failure; } #endif server_common_start(); DAEMON_CLEAR_ERRMSG(); return 0; failure: if (msg != NULL) { fprintf(stderr, "Start failed: %s\n", msg); } DAEMON_CLEAR_ERRMSG(); exit(1); }