/****************************************************************************** * 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: jingrui * Create: 2018-12-01 * Description: provide plugin definition ******************************************************************************/ #include #include #include #include #include #include #include #include "log.h" #include "plugin.h" #include "pspec.h" #include "utils.h" #include "parser.h" #include "buffer.h" #include "isulad_config.h" #include "specs.h" #include "specs_extend.h" #include "rest_common.h" #include "containers_store.h" #include "constants.h" #include "plugin_activate_plugin_request.h" #include "plugin_activate_plugin_response.h" #include "plugin_init_plugin_request.h" #include "plugin_init_plugin_response.h" #include "plugin_event_pre_create_request.h" #include "plugin_event_pre_create_response.h" #include "plugin_event_pre_start_request.h" #include "plugin_event_pre_start_response.h" #include "plugin_event_post_stop_request.h" #include "plugin_event_post_stop_response.h" #include "plugin_event_post_remove_request.h" #include "plugin_event_post_remove_response.h" #define plugin_socket_path "/run/isulad/plugins" #define plugin_socket_file_regex ".*.sock$" // suffix is '.sock' #define PLUGIN_SOCKET_FILE_SUFFIX_LEN 5 #define PLUGIN_ACTIVATE_MAX_RETRY 3 #ifndef RestHttpHead #define RestHttpHead "http://localhost" #endif #define PluginServiceActivate "/PluginService/Activate" #define PluginServiceInit "/PluginService/Init" #define PluginServicePreCreate "/PluginService/PreCreate" #define PluginServicePreStart "/PluginService/PreStart" #define PluginServicePostStop "/PluginService/PostStop" #define PluginServicePostRemove "/PluginService/PostRemove" static int pm_init_plugin(const plugin_t *plugin); static int plugin_event_pre_start_handle(const plugin_t *plugin, const char *cid); static int plugin_event_post_stop_handle(const plugin_t *plugin, const char *cid); static int plugin_event_post_remove_handle(const plugin_t *plugin, const char *cid); enum plugin_action { ACTIVE_PLUGIN, DEACTIVE_PLUGIN }; static int check_err(int err, const char *msg) { if (err) { return -1; } if (msg != NULL && strlen(msg)) { return -1; } return 0; } static char *dup_cid(const container_t *cont) { return util_strdup_s(cont->common_config->id); } /* * return container status, defined by Container_Status. */ static int get_status(const container_t *cont) { return (int)state_get_status(cont->state); } /* * join , seperated string into one. */ static char *join_enable_plugins(const char *plugins) { char *default_plugins = NULL; char *tmp = NULL; char *ep = NULL; default_plugins = conf_get_enable_plugins(); if ((default_plugins == NULL) && (plugins == NULL)) { return NULL; } if (plugins == NULL) { return default_plugins; } if (default_plugins == NULL) { return util_strdup_s(plugins); } tmp = util_string_append(ISULAD_ENABLE_PLUGINS_SEPERATOR, default_plugins); if (tmp == NULL) { ERROR("string append failed %s -> %s", ISULAD_ENABLE_PLUGINS_SEPERATOR, default_plugins); goto out; } ep = util_string_append(plugins, tmp); if (ep == NULL) { ERROR("string append failed %s -> %s", plugins, tmp); goto out; } out: free(default_plugins); free(tmp); return ep; } static char *get_uniq_enable_plugins(const oci_runtime_spec *oci) { char *names = NULL; char *full = NULL; char **raw = NULL; char **arr = NULL; size_t i = 0; if (oci == NULL) { goto failed; } names = oci_container_get_env(oci, ISULAD_ENABLE_PLUGINS); full = join_enable_plugins(names); if (full == NULL) { INFO("no plugins enabled"); goto failed; } raw = util_string_split(full, ISULAD_ENABLE_PLUGINS_SEPERATOR_CHAR); if (raw == NULL) { ERROR("split plugin name failed"); goto failed; } UTIL_FREE_AND_SET_NULL(names); UTIL_FREE_AND_SET_NULL(full); for (i = 0; i < util_array_len((const char **)raw); i++) { if (strings_in_slice((const char **)arr, util_array_len((const char **)arr), raw[i])) { continue; } if (util_array_append(&arr, raw[i]) != 0) { ERROR("append uniq plugin name failed"); goto failed; } } names = util_string_join(ISULAD_ENABLE_PLUGINS_SEPERATOR, (const char **)arr, util_array_len((const char **)arr)); if (names == NULL) { ERROR("join uniq plugin name failed"); goto failed; } full = util_string_append(names, ISULAD_ENABLE_PLUGINS "="); if (full == NULL) { ERROR("init uniq enable plugins env failed"); goto failed; } util_free_array(raw); util_free_array(arr); free(names); return full; failed: util_free_array(raw); util_free_array(arr); free(names); free(full); return NULL; } static int set_env_enable_plugins(oci_runtime_spec *oci) { char *uniq = NULL; if (oci == NULL) { ERROR("BUG oci should not be nil"); goto failed; } if (oci->process == NULL) { ERROR("BUG oci->process should not be nil"); oci->process = util_common_calloc_s(sizeof(defs_process)); if (oci->process == NULL) { ERROR("out of memory"); goto failed; } } uniq = get_uniq_enable_plugins(oci); if (uniq == NULL) { goto failed; } if (util_env_insert(&oci->process->env, &oci->process->env_len, ISULAD_ENABLE_PLUGINS, strlen(ISULAD_ENABLE_PLUGINS), uniq)) { WARN("set env %s failed", uniq); } free(uniq); return 0; failed: free(uniq); return -1; } static char **get_enable_plugins(const char *plugins) { char **arr = NULL; size_t i, arr_len; char **dst = NULL; size_t dst_len = 0; if (plugins == NULL) { return dst; } arr = util_string_split(plugins, ISULAD_ENABLE_PLUGINS_SEPERATOR_CHAR); if (arr == NULL) { ERROR("Out of memory"); goto out; } arr_len = util_array_len((const char **)arr); for (i = 0; i < arr_len; i++) { if (strings_in_slice((const char **)dst, dst_len, arr[i])) { continue; } if (util_array_append(&dst, arr[i]) != 0) { util_free_array(dst); dst = NULL; goto out; } dst_len = util_array_len((const char **)dst); } if (arr_len != dst_len) { ERROR("enable plugins not unique: %s", plugins); } out: util_free_array(arr); return dst; } static uint64_t plugin_get_init_type(const plugin_t *p) { if (p == NULL) { return 0; } if (p->manifest == NULL) { return 0; } return p->manifest->init_type; } static int get_plugin_dir(char *plugin_dir) { int ret = 0; char *statedir = NULL; if (plugin_dir == NULL) { return -1; } statedir = conf_get_isulad_statedir(); if (statedir == NULL) { ERROR("failed get statedir"); return -1; } ret = snprintf(plugin_dir, PATH_MAX, "%s/plugins", statedir); if (ret < 0 || (size_t)ret >= PATH_MAX) { goto failed; } ret = util_mkdir_p(plugin_dir, DEFAULT_SECURE_FILE_MODE); if (ret < 0) { goto failed; } free(statedir); return 0; failed: free(statedir); return -1; } // wait_events wait until inotify static int wait_events(int inotify_fd) { fd_set rfds; FD_ZERO(&rfds); FD_SET(inotify_fd, &rfds); return select(FD_SETSIZE, &rfds, NULL, NULL, NULL); } static int verify_plugin_address(const char *plugin_addr) { struct stat fileStat = { 0 }; // add file permission check?? // only owner root mode 600 is allowed // // check weather it is socket file if (!stat(plugin_addr, &fileStat)) { if (S_ISSOCK(fileStat.st_mode)) { return 0; } else { INFO("Skip %s, mode(%o).", plugin_addr, fileStat.st_mode & S_IFMT); return -1; } } else { ERROR("Failed to get(%s) file stat.", plugin_addr); return -1; } } static int get_plugin_addr_and_name(char *plugin_addr, char *plugin_name, const char *filename, const char *plugin_dir, int action) { size_t str_length = 0; int nret = 0; if (filename == NULL) { return -1; } str_length = strlen(filename); if (util_reg_match(plugin_socket_file_regex, filename)) { ERROR("not plugin filename %s", filename); goto out; } (void)strcpy(plugin_name, filename); plugin_name[str_length - PLUGIN_SOCKET_FILE_SUFFIX_LEN] = 0; nret = snprintf(plugin_addr, PATH_MAX, "%s/%s", plugin_dir, filename); if (nret < 0 || nret >= PATH_MAX) { ERROR("get plugin addr failed %s", filename); goto out; } if (action == DEACTIVE_PLUGIN) { return 0; } return verify_plugin_address(plugin_addr); out: ERROR("invalid plugin socket(%s), skipping..", filename); return -1; } static int pm_activate_plugin_with_retry(plugin_t *plugin, size_t retry) { size_t i = 0; int err = 0; for (i = 0; i < retry; i++) { err = pm_activate_plugin(plugin); if (!err) { return 0; } sleep((unsigned int)i + 1); } return err; } static void pm_rdlock(void) { int errcode; errcode = pthread_rwlock_rdlock(&g_plugin_manager->pm_rwlock); if (errcode != 0) { ERROR("Read lock failed: %s", strerror(errcode)); } } static void pm_wrlock(void) { int errcode; errcode = pthread_rwlock_wrlock(&g_plugin_manager->pm_rwlock); if (errcode != 0) { ERROR("Write lock failed: %s", strerror(errcode)); } } static void pm_unlock(void) { int errcode; errcode = pthread_rwlock_unlock(&g_plugin_manager->pm_rwlock); if (errcode != 0) { ERROR("Unlock failed: %s", strerror(errcode)); } } static void free_plugin(plugin_t *plugin) { if (plugin == NULL) { return; } UTIL_FREE_AND_SET_NULL(plugin->name); UTIL_FREE_AND_SET_NULL(plugin->addr); UTIL_FREE_AND_SET_NULL(plugin->manifest); UTIL_FREE_AND_SET_NULL(plugin->activated_errmsg); free(plugin); } static int do_get_plugin(const char *name, plugin_t **rplugin) { plugin_t *plugin = NULL; pm_rdlock(); plugin = map_search(g_plugin_manager->np, (void *)name); pm_unlock(); *rplugin = plugin; if (plugin == NULL) { return -1; } plugin_get(plugin); return 0; } static int pm_register_plugin(const char *name, const char *addr) { int err; plugin_t *plugin = NULL; /* * this function called in reload_plugin, remember dont call reload_plugin * agaim. */ err = do_get_plugin(name, &plugin); if (err == 0) { /* plugin already exist */ pm_put_plugin(plugin); DEBUG("skip register exist plugin %s", name); return 0; } plugin = plugin_new(name, addr); if (plugin == NULL) { ERROR("alloc plugin failed"); goto failed; } err = pm_activate_plugin_with_retry(plugin, PLUGIN_ACTIVATE_MAX_RETRY); if (err != 0) { ERROR("active plugin failed"); goto failed; } if (plugin_get_init_type(plugin) != PLUGIN_INIT_SKIP) { err = pm_init_plugin(plugin); if (err != 0) { ERROR("init plugin failed"); goto failed; } } err = pm_add_plugin(plugin); if (err != 0) { ERROR("add plugin to map failed"); goto failed; } INFO("add activated plugin %s 0x%x", plugin->name, plugin->manifest->watch_event); return 0; failed: free_plugin(plugin); return -1; } static int pm_unregister_plugin(const char *name, const char *addr) { int err = 0; plugin_t *plugin = NULL; err = pm_get_plugin(name, &plugin); if (err != 0) { ERROR("plugin %s not exist in manager", name); return -1; } err = pm_deactivate_plugin(plugin); if (err != 0) { /* ignore errors */ ERROR("deactivate plugin %s failed", name); } pm_put_plugin(plugin); err = pm_del_plugin(plugin); if (err != 0) { ERROR("can not del plugin %s", name); return -1; } return 0; } static int handle_plugin_event(const char *event_name, const char *plugin_dir, int action) { char addr[PATH_MAX] = { 0 }; char name[PATH_MAX] = { 0 }; if (get_plugin_addr_and_name(addr, name, event_name, plugin_dir, action) < 0) { return -1; } switch (action) { case ACTIVE_PLUGIN: INFO("Activate plugin: %s...", name); pm_register_plugin(name, addr); break; case DEACTIVE_PLUGIN: INFO("Deactivate plugin: %s...", name); pm_unregister_plugin(name, addr); break; default: INFO("Unsupport action, skip..."); } return 0; } static int reload_plugin(const char *name) { char plugin_dir[PATH_MAX] = { 0 }; char filename[PATH_MAX] = { 0 }; int ret = 0; INFO("reload plugin %s ...", name); if (get_plugin_dir(plugin_dir) < 0) { ERROR("get plugin dir failed"); return -1; } ret = snprintf(filename, PATH_MAX, "%s.sock", name); if (ret < 0 || ret >= PATH_MAX) { ERROR("get plugin addr failed %s", filename); return -1; } return handle_plugin_event(filename, plugin_dir, ACTIVE_PLUGIN); } static int scan_existing_plugins(const char *dir) { DIR *midir = NULL; struct dirent *info_archivo = NULL; midir = opendir(dir); if (midir == NULL) { ERROR("scan_existing_plugins : Error in opendir"); return -1; } info_archivo = readdir(midir); while (info_archivo != 0) { // skip . .. if (strncmp(info_archivo->d_name, ".", PATH_MAX) == 0 || strncmp(info_archivo->d_name, "..", PATH_MAX) == 0) { info_archivo = readdir(midir); continue; } handle_plugin_event(info_archivo->d_name, dir, ACTIVE_PLUGIN); info_archivo = readdir(midir); } closedir(midir); return 0; } static int process_plugin_events(int inotify_fd, const char *plugin_dir) { ssize_t events_length = 0; ssize_t events_index = 0; struct inotify_event *plugin_event = NULL; char buffer[8192 + 1] = { 0 }; int action = 0; events_length = read(inotify_fd, buffer, 8192); if (events_length <= 0) { ERROR("Failed to wait events"); return -1; } while (events_index < events_length) { plugin_event = (struct inotify_event *)(&buffer[events_index]); ssize_t event_size = (ssize_t)(plugin_event->len) + (ssize_t)offsetof(struct inotify_event, name); // should deal with events_index > events_length?? if (event_size == 0 || event_size > (events_length - events_index)) { break; } events_index += event_size; if (plugin_event->mask & IN_CREATE) { action = ACTIVE_PLUGIN; } else if (plugin_event->mask & IN_DELETE) { action = DEACTIVE_PLUGIN; } else { continue; } handle_plugin_event(plugin_event->name, plugin_dir, action); } return 0; } /* * plugin_manager_routine manages the lifecycles of plugins * include: discovery, active and deactive * */ static void *plugin_manager_routine(void *arg) { int inotify_fd = 0; int wd = 0; char plugin_dir[PATH_MAX] = { 0 }; int errcode = 0; errcode = pthread_detach(pthread_self()); if (errcode != 0) { ERROR("Detach thread failed: %s", strerror(errcode)); return NULL; } if (pm_init() < 0) { ERROR("init pm failed"); return NULL; } if (get_plugin_dir(plugin_dir) < 0) { ERROR("Failed to create plugin dir"); return NULL; } if (scan_existing_plugins(plugin_dir) < 0) { ERROR("Failed to scan existing plugins"); return NULL; } // initilize inotify instance inotify_fd = inotify_init(); if (inotify_fd < 0) { ERROR("Failed to initalize inotify instance"); return NULL; } // add plugin_dir to watch wd = inotify_add_watch(inotify_fd, plugin_dir, IN_CREATE | IN_DELETE); if (wd < 0) { ERROR("Failed to watch plugin dir"); return NULL; } DEBUG("Watching %s for plugin disovery", plugin_dir); for (;;) { if (wait_events(inotify_fd) < 0) { ERROR("Failed to wait events"); // something abnormal occurs, wait for 1 second and continue sleep(1); continue; } process_plugin_events(inotify_fd, plugin_dir); } } int start_plugin_manager(void) { pthread_t thread = 0; int ret = 0; ret = pthread_create(&thread, NULL, plugin_manager_routine, NULL); if (ret) { ERROR("Thread creation failed"); return -1; } return 0; } static void plugin_rdlock(plugin_t *plugin) { int errcode; errcode = pthread_rwlock_rdlock(&plugin->lock); if (errcode != 0) { ERROR("Plugin read lock failed: %s", strerror(errcode)); } } static void plugin_wrlock(plugin_t *plugin) { int errcode; errcode = pthread_rwlock_wrlock(&plugin->lock); if (errcode != 0) { ERROR("Plugin write lock failed: %s", strerror(errcode)); } } static void plugin_unlock(plugin_t *plugin) { int errcode; errcode = pthread_rwlock_unlock(&plugin->lock); if (errcode != 0) { ERROR("Plugin unlock failed: %s", strerror(errcode)); } } plugin_t *plugin_new(const char *name, const char *addr) { plugin_t *plugin = NULL; int errcode = 0; plugin = util_common_calloc_s(sizeof(plugin_t)); if (plugin == NULL) { goto bad; } errcode = pthread_rwlock_init(&plugin->lock, NULL); if (errcode != 0) { ERROR("Plugin init lock failed: %s", strerror(errcode)); goto bad; } plugin->name = util_strdup_s(name); plugin->addr = util_strdup_s(addr); plugin->manifest = util_common_calloc_s(sizeof(plugin_manifest_t)); if (plugin->manifest == NULL) { goto bad; } return plugin; bad: free_plugin(plugin); return NULL; } int plugin_set_activated(plugin_t *plugin, bool activated, const char *errmsg) { plugin_wrlock(plugin); plugin->activated = activated; if (errmsg != NULL) { plugin->activated_errcnt++; UTIL_FREE_AND_SET_NULL(plugin->activated_errmsg); plugin->activated_errmsg = util_strdup_s(errmsg); } else { plugin->activated_errcnt = 0; UTIL_FREE_AND_SET_NULL(plugin->activated_errmsg); } plugin_unlock(plugin); return 0; } int plugin_set_manifest(plugin_t *plugin, const plugin_manifest_t *manifest) { if (manifest == NULL) { return -1; } plugin_wrlock(plugin); plugin->manifest->init_type = manifest->init_type; plugin->manifest->watch_event = manifest->watch_event; plugin_unlock(plugin); return 0; } void plugin_get(plugin_t *plugin) { if (plugin == NULL) { return; } atomic_int_inc(&plugin->ref); } void plugin_put(plugin_t *plugin) { if (plugin == NULL) { return; } if (!atomic_int_dec_test(&plugin->ref)) { return; } free_plugin(plugin); return; } bool plugin_is_watching(plugin_t *plugin, uint64_t pe) { bool ok = 0; if (plugin == NULL) { ERROR("nil plugin"); return 0; } plugin_rdlock(plugin); if (plugin->manifest == NULL) { ERROR("nil manifest"); ok = 0; } else { ok = plugin->manifest->watch_event & pe; } plugin_unlock(plugin); INFO("plugin %s watching=%s for event 0x%x", plugin->name, (ok ? "true" : "false"), pe); return ok; } static int unpack_activate_response(const struct parsed_http_message *message, void *arg) { int ret = 0; plugin_manifest_t *manifest = arg; plugin_activate_plugin_response *resp = NULL; parser_error err = NULL; ret = check_status_code(message->status_code); if (ret != 0) { ret = -1; goto out; } resp = plugin_activate_plugin_response_parse_data(message->body, NULL, &err); if (resp == NULL) { ERROR("parse activate response failed"); ret = -1; goto out; } if (resp->err_message != NULL) { ERROR("activate response error massge %s", resp->err_message); ret = -1; goto out; } INFO("get resp 0x%x", resp->watch_event); manifest->init_type = resp->init_type; manifest->watch_event = resp->watch_event; out: free(err); free_plugin_activate_plugin_response(resp); return ret; } int pm_activate_plugin(plugin_t *plugin) { int ret = 0; int nret = 0; plugin_activate_plugin_request reqs = { 0 }; char *body = NULL; size_t body_len = 0; struct parser_context ctx = { OPT_GEN_SIMPLIFY, 0, }; parser_error err = NULL; Buffer *output = NULL; char *errmsg = NULL; plugin_manifest_t manifest = { 0 }; char socket[PATH_MAX] = { 0 }; body = plugin_activate_plugin_request_generate_json(&reqs, &ctx, &err); if (body == NULL) { ERROR("marshal activate request to %s failed", plugin->addr); ret = -1; goto out; } body_len = strlen(body) + 1; nret = snprintf(socket, PATH_MAX, "unix://%s", plugin->addr); if (nret < 0 || nret >= PATH_MAX) { ERROR("get plugin socket failed"); ret = -1; goto out; } ret = rest_send_requst(socket, RestHttpHead PluginServiceActivate, body, body_len, &output); if (ret != 0) { ERROR("send activate request to %s failed", plugin->addr); goto out; } ret = get_response(output, unpack_activate_response, (void *)(&manifest)); if (ret != 0) { ERROR("unpack activate response from %s failed", plugin->addr); goto out; } out: plugin_set_activated(plugin, ret == 0, errmsg); plugin_set_manifest(plugin, &manifest); buffer_free(output); free(err); free(body); return ret; } int pm_deactivate_plugin(plugin_t *plugin) { return 0; } static bool plugin_useby_container(const plugin_t *plugin, const container_t *cont) { bool ok = false; char *plugin_names = NULL; char **pnames = NULL; size_t i = 0; if (plugin == NULL || cont == NULL) { return ok; } if (plugin->name == NULL) { return ok; } plugin_names = container_get_env_nolock(cont, ISULAD_ENABLE_PLUGINS); pnames = get_enable_plugins(plugin_names); for (i = 0; i < util_array_len((const char **)pnames); i++) { if (strcmp(pnames[i], plugin->name) == 0) { ok = true; break; } } free(plugin_names); free(pnames); return ok; } static int unpack_init_response(const struct parsed_http_message *message, void *arg) { int ret = 0; plugin_init_plugin_response *resp = NULL; parser_error err = NULL; ret = check_status_code(message->status_code); if (ret != 0) { ret = -1; goto out; } resp = plugin_init_plugin_response_parse_data(message->body, NULL, &err); if (resp == NULL) { ret = -1; ERROR("parse init response failed"); goto out; } ret = check_err(resp->err_code, resp->err_message); if (ret != 0) { isulad_set_error_message(resp->err_message); ERROR("init response error massge (%d)%s", resp->err_code, resp->err_message); goto out; } INFO("plugin init ok"); out: free(err); free_plugin_init_plugin_response(resp); return ret; } /* * add container info into reqs.elem. */ static int pm_prepare_init_reqs(const plugin_t *plugin, plugin_init_plugin_request *reqs, const char *cid) { int ret = 0; container_t *cont = NULL; oci_runtime_spec *ocic = NULL; plugin_init_plugin_request_containers_element *elem = NULL; cont = containers_store_get(cid); if (cont == NULL) { /* container not exist, nothing to do */ return 0; } if (!plugin_useby_container(plugin, cont)) { goto out; } if (plugin_get_init_type(plugin) == PLUGIN_INIT_SKIP) { goto out; } if (plugin_get_init_type(plugin) == PLUGIN_INIT_WITH_CONTAINER_RUNNING && get_status(cont) != CONTAINER_STATUS_RUNNING) { goto out; } elem = util_common_calloc_s(sizeof(plugin_init_plugin_request_containers_element)); if (elem == NULL) { ret = -1; ERROR("Out of memory"); goto out; } elem->id = dup_cid(cont); if (elem->id == NULL) { ret = -1; ERROR("Out of memory"); goto out; } elem->status = get_status(cont); ocic = load_oci_config(cont->root_path, elem->id); if (ocic == NULL) { ret = -1; ERROR("read oci config failed"); goto out; } elem->pspec = get_pspec(ocic); if (elem->pspec == NULL) { ret = -1; ERROR("marshal pspec failed"); goto out; } reqs->containers[reqs->containers_len] = elem; elem = NULL; reqs->containers_len++; out: free_plugin_init_plugin_request_containers_element(elem); container_unref(cont); free_oci_runtime_spec(ocic); return ret; } static int pm_init_plugin(const plugin_t *plugin) { int ret = 0; int nret = 0; char **cnames = NULL; size_t container_num = 0; plugin_init_plugin_request reqs = { 0 }; char *body = NULL; size_t body_len = 0; struct parser_context ctx = { OPT_GEN_SIMPLIFY, 0, }; parser_error err = NULL; Buffer *output = NULL; char socket[PATH_MAX] = { 0 }; size_t i = 0; cnames = containers_store_list_ids(); container_num = util_array_len((const char **)cnames); /* * send init request no matter containers exist or not, plugin should * prepare or delete dirty resource. */ if (container_num) { if (container_num > SIZE_MAX / sizeof(plugin_init_plugin_request_containers_element *)) { ERROR("Invalid container nums"); ret = -1; goto out; } reqs.containers = util_common_calloc_s(container_num * sizeof(plugin_init_plugin_request_containers_element *)); if (reqs.containers == NULL) { ERROR("Out of memory"); ret = -1; goto out; } } /* * add elem to reqs, if no containers availabe add no elem. */ for (i = 0; i < container_num; i++) { ret = pm_prepare_init_reqs(plugin, &reqs, cnames[i]); if (ret != 0) { ret = -1; ERROR("failed prepare init reqs"); goto out; } } body = plugin_init_plugin_request_generate_json(&reqs, &ctx, &err); if (body == NULL) { ERROR("marshal plugin init request to %s failed %s", plugin->addr, err); ret = -1; goto out; } body_len = strlen(body) + 1; nret = snprintf(socket, PATH_MAX, "unix://%s", plugin->addr); if (nret < 0 || nret >= PATH_MAX) { ERROR("get plugin socket failed %s", plugin->addr); ret = -1; goto out; } ret = rest_send_requst(socket, RestHttpHead PluginServiceInit, body, body_len, &output); if (ret != 0) { ret = -1; ERROR("plugin init request to %s failed", plugin->addr); goto out; } ret = get_response(output, unpack_init_response, NULL); if (ret != 0) { ret = -1; ERROR("unpack plugin init response from %s failed", plugin->addr); goto out; } out: util_free_array(cnames); cnames = NULL; for (i = 0; i < reqs.containers_len; i++) { UTIL_FREE_AND_SET_NULL(reqs.containers[i]->id); UTIL_FREE_AND_SET_NULL(reqs.containers[i]->pspec); UTIL_FREE_AND_SET_NULL(reqs.containers[i]); } UTIL_FREE_AND_SET_NULL(reqs.containers); buffer_free(output); free(err); free(body); return ret; } int pm_add_plugin(plugin_t *plugin) { int ok = 0; pm_wrlock(); ok = map_insert(g_plugin_manager->np, (void *)plugin->name, plugin); pm_unlock(); if (!ok) { return -1; } /* plugin_put() called in pm_del_plugin() */ plugin_get(plugin); return 0; } int pm_del_plugin(const plugin_t *plugin) { int ok; pm_wrlock(); /* plugin_put() called in map_remove() by pm_np_item_free() */ ok = map_remove(g_plugin_manager->np, (void *)plugin->name); pm_unlock(); if (!ok) { return -1; } return 0; } int pm_get_plugin(const char *name, plugin_t **rplugin) { if (do_get_plugin(name, rplugin) == 0) { return 0; } if (reload_plugin(name)) { return -1; } return do_get_plugin(name, rplugin); } void pm_put_plugin(plugin_t *plugin) { plugin_put(plugin); } int pm_get_plugins_nolock(uint64_t pe, plugin_t ***rplugins, size_t *count) { int ret = 0; int i = 0; size_t size = 0; plugin_t **plugins = NULL; map_itor *itor = NULL; size = map_size(g_plugin_manager->np); if (size == 0) { /* empty */ return 0; } if (size > SIZE_MAX / sizeof(plugin_t *)) { ret = -1; ERROR("Invalid plugins size"); goto out; } plugins = util_common_calloc_s(sizeof(plugin_t *) * size); if (plugins == NULL) { ret = -1; ERROR("Out of memory"); goto out; } itor = map_itor_new(g_plugin_manager->np); if (itor == NULL) { ret = -1; ERROR("Out of memory"); goto out; } for (i = 0; i < (int)size && map_itor_valid(itor); i++, map_itor_next(itor)) { plugins[i] = map_itor_value(itor); /* plugin_put() called in pm_put_plugins() */ plugin_get(plugins[i]); } *rplugins = plugins; *count = (size_t)i; out: map_itor_free(itor); itor = NULL; if (ret < 0) { UTIL_FREE_AND_SET_NULL(plugins); } return ret; } static void pm_np_item_free(void *key, void *val) { plugin_t *plugin = val; free(key); plugin_put(plugin); } static void pm_free(plugin_manager_t *gpm) { if (gpm == NULL) { return; } map_free(gpm->np); pthread_rwlock_destroy(&gpm->pm_rwlock); free(gpm); } int pm_init(void) { int ret = 0; plugin_manager_t *gpm = NULL; if (g_plugin_manager != NULL) { return 0; } gpm = util_common_calloc_s(sizeof(plugin_manager_t)); if (gpm == NULL) { return -1; } ret = pthread_rwlock_init(&gpm->pm_rwlock, NULL); if (ret != 0) { ret = -1; goto bad; } gpm->np = map_new(MAP_STR_PTR, MAP_DEFAULT_CMP_FUNC, pm_np_item_free); if (gpm->np == NULL) { goto bad; } g_plugin_manager = gpm; return 0; bad: pm_free(gpm); gpm = NULL; return -1; } static int plugin_event_handle_dispath_impl(const char *cid, const char *plugins, uint64_t pe) { int ret = 0; plugin_t *plugin = NULL; char **pnames = NULL; size_t i = 0; pnames = get_enable_plugins(plugins); if (pnames == NULL) { goto out; } pm_rdlock(); for (i = 0; i < util_array_len((const char **)pnames); i++) { if (pm_get_plugin(pnames[i], &plugin)) { /* plugin not found */ ERROR("plugin %s not registered.", pnames[i]); ret = -1; continue; } if (!plugin_is_watching(plugin, pe)) { pm_put_plugin(plugin); continue; } switch (pe) { case PLUGIN_EVENT_CONTAINER_PRE_START: ret = plugin_event_pre_start_handle(plugin, cid); break; case PLUGIN_EVENT_CONTAINER_POST_STOP: ret = plugin_event_post_stop_handle(plugin, cid); break; case PLUGIN_EVENT_CONTAINER_POST_REMOVE: ret = plugin_event_post_remove_handle(plugin, cid); break; default: ERROR("plugin event %d not support.", pe); ret = -1; break; } pm_put_plugin(plugin); if (ret != 0) { ret = -1; continue; } } pm_unlock(); out: util_free_array(pnames); return ret; } static int plugin_event_handle_dispath(const container_t *cont, uint64_t pe) { int ret = 0; char *cid = NULL; char *plugins = NULL; cid = dup_cid(cont); if (cid == NULL) { return 0; } plugins = container_get_env_nolock(cont, ISULAD_ENABLE_PLUGINS); ret = plugin_event_handle_dispath_impl(cid, plugins, pe); free(cid); free(plugins); return ret; } static int unpack_event_pre_create_response(const struct parsed_http_message *message, void *arg) { int ret = 0; char **pspec = arg; char *dst = NULL; plugin_event_pre_create_response *resp = NULL; parser_error err = NULL; ret = check_status_code(message->status_code); if (ret != 0) { ret = -1; goto out; } resp = plugin_event_pre_create_response_parse_data(message->body, NULL, &err); if (resp == NULL) { ERROR("parse pre-create response failed"); ret = -1; goto out; } ret = check_err(resp->err_code, resp->err_message); if (ret) { isulad_set_error_message(resp->err_message); ret = -1; ERROR("pre-create response error massge (%d)%s", resp->err_code, resp->err_message); goto out; } INFO("pre-create %s ok", resp->id); if (resp->pspec == NULL) { ret = -1; ERROR("plugin pre-create response missing pspec"); goto out; } dst = merge_pspec(*pspec, resp->pspec); if (dst == NULL) { ERROR("plugin pre-create failed to merge pspec"); goto out; } *pspec = dst; dst = NULL; out: free(dst); free(err); free_plugin_event_pre_create_response(resp); return ret; } static int plugin_event_pre_create_handle(const plugin_t *plugin, const char *cid, char **base) { int ret = 0; int nret = 0; char *body = NULL; size_t body_len = 0; struct parser_context ctx = { OPT_GEN_SIMPLIFY, 0, }; parser_error err = NULL; Buffer *output = NULL; char *dst = NULL; char *new = NULL; char socket[PATH_MAX] = { 0 }; plugin_event_pre_create_request reqs = { 0 }; reqs.id = (char *)cid; reqs.pspec = *base; body = plugin_event_pre_create_request_generate_json(&reqs, &ctx, &err); if (body == NULL) { ERROR("marshal event precreate request to %s failed", plugin->addr); ret = -1; goto out; } body_len = strlen(body) + 1; nret = snprintf(socket, sizeof(socket), "unix://%s", plugin->addr); if (nret < 0 || (size_t)nret >= sizeof(socket)) { ERROR("get plugin socket failed %s", plugin->addr); ret = -1; goto out; } ret = rest_send_requst(socket, RestHttpHead PluginServicePreCreate, body, body_len, &output); if (ret != 0) { ret = -1; ERROR("send event precreate request to %s failed", plugin->addr); goto out; } ret = get_response(output, unpack_event_pre_create_response, (void *)(&new)); if (ret != 0) { ret = -1; ERROR("unpack event precreate response from %s failed", plugin->addr); goto out; } dst = merge_pspec(*base, new); if (dst == NULL) { ret = -1; ERROR("update pspec json failed"); goto out; } free(*base); *base = dst; dst = NULL; out: free(dst); free(new); buffer_free(output); free(err); free(body); return ret; } int plugin_event_container_pre_create(const char *cid, oci_runtime_spec *ocic) { int ret = 0; plugin_t *plugin = NULL; char **pnames = NULL; size_t i = 0; char *plugin_names = NULL; char *pspec = NULL; if (cid == NULL) { ERROR("cid is nil pointer"); return -1; } if (ocic == NULL) { ERROR("oci spec nil pointer"); return -1; } if (ocic->process == NULL) { ERROR("oci spec missing process field"); return -1; } set_env_enable_plugins(ocic); plugin_names = oci_container_get_env(ocic, ISULAD_ENABLE_PLUGINS); pnames = get_enable_plugins(plugin_names); if (pnames == NULL) { goto out; } pspec = get_pspec(ocic); if (pspec == NULL) { ret = -1; ERROR("failed generate json for pspec"); goto out; } pm_rdlock(); for (i = 0; i < util_array_len((const char **)pnames); i++) { if (pm_get_plugin(pnames[i], &plugin)) { /* plugin not found */ ERROR("plugin %s not registered.", pnames[i]); ret = -1; break; } if (!plugin_is_watching(plugin, (uint64_t)PLUGIN_EVENT_CONTAINER_PRE_CREATE)) { pm_put_plugin(plugin); continue; } ret = plugin_event_pre_create_handle(plugin, cid, &pspec); pm_put_plugin(plugin); if (ret != 0) { ret = -1; break; } } pm_unlock(); if (ret == 0) { /* all plugins works fine */ ret = set_pspec(ocic, pspec); if (ret != 0) { ERROR("plugin pre-create failed to set pspec into oci"); } } else { ERROR("plugin pre-create failed"); } out: free(pspec); free(plugin_names); util_free_array(pnames); return ret; } static int unpack_event_pre_start_response(const struct parsed_http_message *message, void *arg) { int ret = 0; plugin_event_pre_start_response *resp = NULL; parser_error err = NULL; ret = check_status_code(message->status_code); if (ret != 0) { ret = -1; goto out; } resp = plugin_event_pre_start_response_parse_data(message->body, NULL, &err); if (resp == NULL) { ret = -1; ERROR("parse pre-start response failed"); goto out; } ret = check_err(resp->err_code, resp->err_message); if (ret != 0) { isulad_set_error_message(resp->err_message); ERROR("pre-start response error massge (%d)%s", resp->err_code, resp->err_message); goto out; } INFO("pre-start %s ok", resp->id); out: free(err); free_plugin_event_pre_start_response(resp); return ret; } static int plugin_event_pre_start_handle(const plugin_t *plugin, const char *cid) { int ret = 0; int nret = 0; char *body = NULL; size_t body_len = 0; struct parser_context ctx = { OPT_GEN_SIMPLIFY, 0, }; parser_error err = NULL; Buffer *output = NULL; char socket[PATH_MAX] = { 0 }; plugin_event_pre_start_request reqs = { 0 }; reqs.id = (char *)cid; body = plugin_event_pre_start_request_generate_json(&reqs, &ctx, &err); if (body == NULL) { ERROR("marshal event prestart request to %s failed", plugin->addr); ret = -1; goto out; } body_len = strlen(body) + 1; nret = snprintf(socket, sizeof(socket), "unix://%s", plugin->addr); if (nret < 0 || (size_t)nret >= sizeof(socket)) { ERROR("get plugin socket failed %s", plugin->addr); ret = -1; goto out; } ret = rest_send_requst(socket, RestHttpHead PluginServicePreStart, body, body_len, &output); if (ret != 0) { ret = -1; ERROR("send event prestart request to %s failed", plugin->addr); goto out; } ret = get_response(output, unpack_event_pre_start_response, NULL); if (ret != 0) { ret = -1; ERROR("unpack event prestart response from %s failed", plugin->addr); goto out; } out: buffer_free(output); free(err); free(body); return ret; } int plugin_event_container_pre_start(const container_t *cont) { if (cont == NULL) { ERROR("container nil pointer"); return 0; } return plugin_event_handle_dispath(cont, (uint64_t)PLUGIN_EVENT_CONTAINER_PRE_START); } static int unpack_event_post_stop_response(const struct parsed_http_message *message, void *arg) { int ret = 0; plugin_event_post_stop_response *resp = NULL; parser_error err = NULL; ret = check_status_code(message->status_code); if (ret != 0) { ret = -1; goto out; } resp = plugin_event_post_stop_response_parse_data(message->body, NULL, &err); if (resp == NULL) { ret = -1; ERROR("plugin event post_stop response parse failed"); goto out; } ret = check_err(resp->err_code, resp->err_message); if (ret != 0) { isulad_set_error_message(resp->err_message); ERROR("post-stop response error massge (%d)%s", resp->err_code, resp->err_message); goto out; } INFO("post-stop %s ok", resp->id); out: free(err); free_plugin_event_post_stop_response(resp); return ret; } static int plugin_event_post_stop_handle(const plugin_t *plugin, const char *cid) { int ret = 0; int nret = 0; char *body = NULL; size_t body_len = 0; struct parser_context ctx = { OPT_GEN_SIMPLIFY, 0, }; parser_error err = NULL; Buffer *output = NULL; char socket[PATH_MAX] = { 0 }; plugin_event_post_stop_request reqs = { 0 }; reqs.id = (char *)cid; body = plugin_event_post_stop_request_generate_json(&reqs, &ctx, &err); if (body == NULL) { ERROR("marshal event post_stop request to %s failed", plugin->addr); ret = -1; goto out; } body_len = strlen(body) + 1; nret = snprintf(socket, sizeof(socket), "unix://%s", plugin->addr); if (nret < 0 || (size_t)nret >= sizeof(socket)) { ERROR("get plugin socket failed %s", plugin->addr); ret = -1; goto out; } ret = rest_send_requst(socket, RestHttpHead PluginServicePostStop, body, body_len, &output); if (ret != 0) { ret = -1; ERROR("send event post_stop request to %s failed", plugin->addr); goto out; } ret = get_response(output, unpack_event_post_stop_response, NULL); if (ret != 0) { ret = -1; ERROR("unpack event post_stop response from %s failed", plugin->addr); goto out; } out: buffer_free(output); free(err); free(body); return ret; } int plugin_event_container_post_stop(const container_t *cont) { if (cont == NULL) { ERROR("container nil pointer"); return 0; } return plugin_event_handle_dispath(cont, (uint64_t)PLUGIN_EVENT_CONTAINER_POST_STOP); } static int unpack_event_post_remove_response(const struct parsed_http_message *message, void *arg) { int ret = 0; plugin_event_post_remove_response *resp = NULL; parser_error err = NULL; ret = check_status_code(message->status_code); if (ret != 0) { ret = -1; goto out; } resp = plugin_event_post_remove_response_parse_data(message->body, NULL, &err); if (resp == NULL) { ret = -1; ERROR("plugin event post_remove response parse failed"); goto out; } ret = check_err(resp->err_code, resp->err_message); if (ret != 0) { isulad_set_error_message(resp->err_message); ERROR("post-remove response error massge (%d)%s", resp->err_code, resp->err_message); goto out; } INFO("post-remove %s ok", resp->id); out: free(err); free_plugin_event_post_remove_response(resp); return ret; } static int plugin_event_post_remove_handle(const plugin_t *plugin, const char *cid) { int ret = 0; int nret = 0; char *body = NULL; size_t body_len = 0; struct parser_context ctx = { OPT_GEN_SIMPLIFY, 0, }; parser_error err = NULL; Buffer *output = NULL; char socket[PATH_MAX] = { 0 }; plugin_event_post_remove_request reqs = { 0 }; reqs.id = (char *)cid; body = plugin_event_post_remove_request_generate_json(&reqs, &ctx, &err); if (body == NULL) { ERROR("marshal event post_remove request to %s failed", plugin->addr); ret = -1; goto out; } body_len = strlen(body) + 1; nret = snprintf(socket, sizeof(socket), "unix://%s", plugin->addr); if (nret < 0 || (size_t)nret >= sizeof(socket)) { ERROR("get plugin socket failed %s", plugin->addr); ret = -1; goto out; } ret = rest_send_requst(socket, RestHttpHead PluginServicePostRemove, body, body_len, &output); if (ret != 0) { ret = -1; ERROR("send event post_remove request to %s failed", plugin->addr); goto out; } ret = get_response(output, unpack_event_post_remove_response, NULL); if (ret != 0) { ret = -1; ERROR("unpack event post_remove response from %s failed", plugin->addr); goto out; } out: buffer_free(output); free(err); free(body); return ret; } int plugin_event_container_post_remove(const container_t *cont) { if (cont == NULL) { ERROR("container nil pointer"); return 0; } return plugin_event_handle_dispath(cont, (uint64_t)PLUGIN_EVENT_CONTAINER_POST_REMOVE); } int plugin_event_container_post_remove2(const char *cid, const oci_runtime_spec *oci) { char *plugins = NULL; char *cidx = NULL; int ret = 0; if (cid == NULL) { ERROR("cid nil pointer"); return 0; } if (oci == NULL) { ERROR("oci nil pointer"); return 0; } if (oci->process == NULL) { ERROR("oci->process nil pointer"); return 0; } plugins = oci_container_get_env(oci, ISULAD_ENABLE_PLUGINS); cidx = util_strdup_s(cid); if (cidx == NULL) { ERROR("out of memory"); goto out; } ret = plugin_event_handle_dispath_impl(cidx, plugins, (uint64_t)PLUGIN_EVENT_CONTAINER_POST_REMOVE); out: free(cidx); free(plugins); return ret; }