From c954f457d9e7cfe37136eaeb285be93ff8cc64cd Mon Sep 17 00:00:00 2001 From: Nathan Hjelm Date: Wed, 8 Apr 2015 12:00:13 -0600 Subject: [PATCH] mca/base: update the way dynamic components are handled This commit is a rework of the component repository. The changes included in this commit are: - Remove the component dependency code based off .ompi_info files. This code is legacy code dating back 10 years that and is no longer used. - Move the plugin scanning code to the component repository. New calls have been added to add new scanning paths, query available components, and dlopen/load components. - Pass the framework down to mca_base_component_find/filter. Eventually the framework structure will be used to further validate components before they are used. - Add support to the MCA framework system to disable scanning for dlopened components on open (support already existed in register). This is really only relevant to installdirs as it has no register function and no DSO components. Signed-off-by: Nathan Hjelm --- opal/mca/base/base.h | 10 +- opal/mca/base/mca_base_component_find.c | 782 ++---------------- opal/mca/base/mca_base_component_repository.c | 628 ++++++++------ opal/mca/base/mca_base_component_repository.h | 97 ++- opal/mca/base/mca_base_components_close.c | 2 +- opal/mca/base/mca_base_components_open.c | 62 +- opal/mca/base/mca_base_components_register.c | 40 +- opal/mca/base/mca_base_framework.c | 8 + opal/mca/base/mca_base_framework.h | 6 +- .../base/installdirs_base_components.c | 2 +- opal/runtime/opal_finalize.c | 5 +- opal/runtime/opal_init.c | 14 +- 12 files changed, 557 insertions(+), 1099 deletions(-) diff --git a/opal/mca/base/base.h b/opal/mca/base/base.h index 3ba82cb1024..936f72684b9 100644 --- a/opal/mca/base/base.h +++ b/opal/mca/base/base.h @@ -145,11 +145,8 @@ OPAL_DECLSPEC char * mca_base_component_to_string(const mca_base_component_t *a) /* mca_base_component_find.c */ -OPAL_DECLSPEC int mca_base_component_find(const char *directory, const char *type, - const mca_base_component_t *static_components[], - const char *requested_components, - opal_list_t *found_components, - bool open_dso_components); +OPAL_DECLSPEC int mca_base_component_find (const char *directory, mca_base_framework_t *framework, + bool ignore_requested, bool open_dso_components); /** * Parse the requested component string and return an opal_argv of the requested @@ -176,8 +173,7 @@ int mca_base_component_parse_requested (const char *requested, bool *include_mod * This function closes and releases any components that do not match the filter_name and * filter flags. */ -OPAL_DECLSPEC int mca_base_components_filter (const char *framework_name, opal_list_t *components, int output_id, - const char *filter_names, uint32_t filter_flags); +OPAL_DECLSPEC int mca_base_components_filter (mca_base_framework_t *framework, uint32_t filter_flags); diff --git a/opal/mca/base/mca_base_component_find.c b/opal/mca/base/mca_base_component_find.c index a1e7f1b928d..67e6d14853c 100644 --- a/opal/mca/base/mca_base_component_find.c +++ b/opal/mca/base/mca_base_component_find.c @@ -55,75 +55,16 @@ #include "opal/constants.h" #include "opal/mca/dl/base/base.h" - -#if OPAL_HAVE_DL_SUPPORT -/* - * Private types; only necessary when we're dlopening components. - */ -typedef enum component_status { - UNVISITED, - FAILED_TO_LOAD, - CHECKING_CYCLE, - LOADED, - - STATUS_MAX -} component_status_t; - -struct component_file_item_t { - opal_list_item_t super; - - char type[MCA_BASE_MAX_TYPE_NAME_LEN + 1]; - char name[MCA_BASE_MAX_COMPONENT_NAME_LEN + 1]; - char basename[OPAL_PATH_MAX + 1]; - char filename[OPAL_PATH_MAX + 1]; - component_status_t status; -}; -typedef struct component_file_item_t component_file_item_t; - -static OBJ_CLASS_INSTANCE(component_file_item_t, opal_list_item_t, NULL, NULL); - -struct dependency_item_t { - opal_list_item_t super; - - component_file_item_t *di_component_file_item; -}; -typedef struct dependency_item_t dependency_item_t; - -static OBJ_CLASS_INSTANCE(dependency_item_t, opal_list_item_t, NULL, NULL); - -#endif /* OPAL_HAVE_DL_SUPPORT */ - - #if OPAL_HAVE_DL_SUPPORT /* * Private functions */ -static void find_dyn_components(const char *path, const char *type, - const char **names, bool include_mode, - opal_list_t *found_components); -static int save_filename(const char *filename, void *data); -static int open_component(component_file_item_t *target_file, - opal_list_t *found_components); -static int check_opal_info(component_file_item_t *target_file, - opal_list_t *dependencies, - opal_list_t *found_components); -static int check_dependency(char *line, component_file_item_t *target_file, - opal_list_t *dependencies, - opal_list_t *found_components); -static void free_dependency_list(opal_list_t *dependencies); +static void find_dyn_components(const char *path, mca_base_framework_t *framework, + const char **names, bool include_mode); -/* - * Private variables - */ -static const char *opal_info_suffix = ".ompi_info"; -static const char *key_dependency = "dependency="; -static const char component_template[] = "mca_%s_"; -static opal_list_t found_files; -static char **found_filenames = NULL; -static char *last_path_to_use = NULL; #endif /* OPAL_HAVE_DL_SUPPORT */ -static int component_find_check (const char *framework_name, char **requested_component_names, opal_list_t *components); +static int component_find_check (mca_base_framework_t *framework, char **requested_component_names); /* * Dummy structure for casting for open_only logic @@ -152,55 +93,54 @@ static bool use_component(const bool include_mode, * Return one consolidated array of (mca_base_component_t*) pointing to all * available components. */ -int mca_base_component_find(const char *directory, const char *type, - const mca_base_component_t *static_components[], - const char *requested_components, - opal_list_t *found_components, - bool open_dso_components) +int mca_base_component_find (const char *directory, mca_base_framework_t *framework, + bool ignore_requested, bool open_dso_components) { + const mca_base_component_t **static_components = framework->framework_static_components; char **requested_component_names = NULL; mca_base_component_list_item_t *cli; - bool include_mode; - int i, ret; + bool include_mode = true; + int ret; - ret = mca_base_component_parse_requested (requested_components, &include_mode, - &requested_component_names); - if (OPAL_SUCCESS != ret) { - return ret; + if (!ignore_requested) { + ret = mca_base_component_parse_requested (framework->framework_selection, &include_mode, + &requested_component_names); + if (OPAL_SUCCESS != ret) { + return ret; + } } /* Find all the components that were statically linked in */ - OBJ_CONSTRUCT(found_components, opal_list_t); - for (i = 0; NULL != static_components && - NULL != static_components[i]; ++i) { - if ( use_component(include_mode, - (const char**)requested_component_names, - static_components[i]->mca_component_name) ) { - cli = OBJ_NEW(mca_base_component_list_item_t); - if (NULL == cli) { - ret = OPAL_ERR_OUT_OF_RESOURCE; - goto component_find_out; + if (static_components) { + for (int i = 0 ; NULL != static_components[i]; ++i) { + if ( use_component(include_mode, + (const char**)requested_component_names, + static_components[i]->mca_component_name) ) { + cli = OBJ_NEW(mca_base_component_list_item_t); + if (NULL == cli) { + ret = OPAL_ERR_OUT_OF_RESOURCE; + goto component_find_out; + } + cli->cli_component = static_components[i]; + opal_list_append(&framework->framework_components, (opal_list_item_t *) cli); } - cli->cli_component = static_components[i]; - opal_list_append(found_components, (opal_list_item_t *) cli); } } #if OPAL_HAVE_DL_SUPPORT /* Find any available dynamic components in the specified directory */ if (open_dso_components && !mca_base_component_disable_dlopen) { - find_dyn_components(directory, type, - (const char**)requested_component_names, - include_mode, found_components); + find_dyn_components(directory, framework, (const char**)requested_component_names, + include_mode); } else { opal_output_verbose(40, 0, "mca: base: component_find: dso loading for %s MCA components disabled", - type); + framework->framework_name); } #endif if (include_mode) { - ret = component_find_check (type, requested_component_names, found_components); + ret = component_find_check (framework, requested_component_names); } else { ret = OPAL_SUCCESS; } @@ -218,22 +158,13 @@ int mca_base_component_find(const char *directory, const char *type, int mca_base_component_find_finalize(void) { -#if OPAL_HAVE_DL_SUPPORT - if (NULL != found_filenames) { - opal_argv_free(found_filenames); - found_filenames = NULL; - } - if (NULL != last_path_to_use) { - free(last_path_to_use); - last_path_to_use = NULL; - } -#endif return OPAL_SUCCESS; } -int mca_base_components_filter (const char *framework_name, opal_list_t *components, int output_id, - const char *filter_names, uint32_t filter_flags) +int mca_base_components_filter (mca_base_framework_t *framework, uint32_t filter_flags) { + opal_list_t *components = &framework->framework_components; + int output_id = framework->framework_output; mca_base_component_list_item_t *cli, *next; char **requested_component_names = NULL; bool include_mode, can_use; @@ -241,12 +172,12 @@ int mca_base_components_filter (const char *framework_name, opal_list_t *compone assert (NULL != components); - if (0 == filter_flags && NULL == filter_names) { + if (0 == filter_flags && NULL == framework->framework_selection) { return OPAL_SUCCESS; } - ret = mca_base_component_parse_requested (filter_names, &include_mode, - &requested_component_names); + ret = mca_base_component_parse_requested (framework->framework_selection, &include_mode, + &requested_component_names); if (OPAL_SUCCESS != ret) { return ret; } @@ -284,7 +215,7 @@ int mca_base_components_filter (const char *framework_name, opal_list_t *compone } if (include_mode) { - ret = component_find_check (framework_name, requested_component_names, components); + ret = component_find_check (framework, requested_component_names); } else { ret = OPAL_SUCCESS; } @@ -306,625 +237,31 @@ int mca_base_components_filter (const char *framework_name, opal_list_t *compone * need to look at companion .ompi_info files in the same directory as * the library to generate dependencies, etc. */ -static void find_dyn_components(const char *path, const char *type_name, - const char **names, bool include_mode, - opal_list_t *found_components) +static void find_dyn_components(const char *path, mca_base_framework_t *framework, + const char **names, bool include_mode) { - int i, len; - char *path_to_use = NULL, *dir, *end; - component_file_item_t *file; - opal_list_item_t *cur; - char prefix[32 + MCA_BASE_MAX_TYPE_NAME_LEN], *basename; - - /* If path is NULL, iterate over the set of directories specified by - the MCA param mca_base_component_path. If path is not NULL, then - use that as the path. */ - - if (NULL == path) { - if (NULL != mca_base_component_path) { - path_to_use = strdup (mca_base_component_path); - } else { - /* If there's no path, then there's nothing to search -- we're - done */ - return; - } - } else { - path_to_use = strdup(path); - } - if (NULL == path_to_use) { - /* out of memory */ - return; - } - - /* If we haven't done so already, iterate over all the files in - the directories in the path and make a master array of all the - matching filenames that we find. Save the filenames in an - argv-style array. Re-scan do this if the mca_component_path - has changed. */ - if (NULL == found_filenames || - (NULL != last_path_to_use && - 0 != strcmp(path_to_use, last_path_to_use))) { - if (NULL != found_filenames) { - opal_argv_free(found_filenames); - found_filenames = NULL; - free(last_path_to_use); - last_path_to_use = NULL; - } - if (NULL == last_path_to_use) { - last_path_to_use = strdup(path_to_use); - } + mca_base_component_repository_item_t *ri; + opal_list_t *dy_components; + int ret; - dir = path_to_use; - if (NULL != dir) { - do { - end = strchr(dir, OPAL_ENV_SEP); - if (NULL != end) { - *end = '\0'; - } - if ((0 == strcmp(dir, "USER_DEFAULT") || - 0 == strcmp(dir, "USR_DEFAULT")) - && NULL != mca_base_user_default_path) { - if (0 != opal_dl_foreachfile(mca_base_user_default_path, - save_filename, NULL)) { - break; - } - } else if (0 == strcmp(dir, "SYS_DEFAULT") || - 0 == strcmp(dir, "SYSTEM_DEFAULT")) { - if (0 != opal_dl_foreachfile(mca_base_system_default_path, - save_filename, NULL)) { - break; - } - } else { - if (0 != opal_dl_foreachfile(dir, save_filename, NULL)) { - break; - } - } - dir = end + 1; - } while (NULL != end); - } - } - - /* Look through the list of found files and find those that match - the desired framework name */ - snprintf(prefix, sizeof(prefix) - 1, component_template, type_name); - len = strlen(prefix); - OBJ_CONSTRUCT(&found_files, opal_list_t); - for (i = 0; NULL != found_filenames && NULL != found_filenames[i]; ++i) { - basename = strrchr(found_filenames[i], '/'); - if (NULL == basename) { - basename = found_filenames[i]; - } else { - basename += 1; - } - - if (0 != strncmp(basename, prefix, len)) { - continue; - } - - /* We found a match; save all the relevant details in the - found_files list */ - file = OBJ_NEW(component_file_item_t); - if (NULL == file) { - free(path_to_use); + if (NULL != path) { + ret = mca_base_component_repository_add (path); + if (OPAL_SUCCESS != ret) { return; } - strncpy(file->type, type_name, MCA_BASE_MAX_TYPE_NAME_LEN); - file->type[MCA_BASE_MAX_TYPE_NAME_LEN] = '\0'; - strncpy(file->name, basename + len, MCA_BASE_MAX_COMPONENT_NAME_LEN); - file->name[MCA_BASE_MAX_COMPONENT_NAME_LEN] = '\0'; - strncpy(file->basename, basename, OPAL_PATH_MAX); - file->basename[OPAL_PATH_MAX] = '\0'; - strncpy(file->filename, found_filenames[i], OPAL_PATH_MAX); - file->filename[OPAL_PATH_MAX] = '\0'; - file->status = UNVISITED; - - opal_list_append(&found_files, (opal_list_item_t *) - file); - } - - /* Iterate through all the filenames that we found that matched - the framework we were looking for. Since one component may - [try to] call another to be loaded, only try to load the - UNVISITED files. Also, ignore the return code -- basically, - give every file one chance to try to load. If they load, - great. If not, great. */ - for (cur = opal_list_get_first(&found_files); - opal_list_get_end(&found_files) != cur; - cur = opal_list_get_next(cur)) { - file = (component_file_item_t *) cur; - - if( UNVISITED == file->status ) { - bool op = true; - file->status = CHECKING_CYCLE; - - op = use_component(include_mode, names, file->name); - if( true == op ) { - open_component(file, found_components); - } - } - } - - /* So now we have a final list of loaded components. We can free all - the file information. */ - for (cur = opal_list_remove_first(&found_files); - NULL != cur; - cur = opal_list_remove_first(&found_files)) { - OBJ_RELEASE(cur); - } - OBJ_DESTRUCT(&found_files); - - /* All done, now let's cleanup */ - free(path_to_use); -} - - -/* - * Blindly save all filenames into an argv-style list. This function - * is the callback from lt_dlforeachfile(). - */ -static int save_filename(const char *filename, void *data) -{ - opal_argv_append_nosize(&found_filenames, filename); - return 0; -} - - -static int file_exists(const char *filename, const char *ext) -{ - char *final; - struct stat buf; - int ret; - - if (NULL != ext) { - asprintf(&final, "%s.%s", filename, ext); - } else { - final = strdup(filename); - } - if (NULL == final) { - return 0; } - ret = stat(final, &buf); - free(final); - return (0 == ret ? 1 : 0); -} - - -/* - * Open a component, chasing down its dependencies first, if possible. - */ -static int open_component(component_file_item_t *target_file, - opal_list_t *found_components) -{ - opal_dl_handle_t *component_handle; - mca_base_component_t *component_struct; - char *struct_name; - opal_list_t dependencies; - opal_list_item_t *cur; - mca_base_component_list_item_t *mitem; - dependency_item_t *ditem; - size_t len; - int vl; - - opal_output_verbose(40, 0, "mca: base: component_find: examining dyanmic %s MCA component \"%s\"", - target_file->type, target_file->name); - opal_output_verbose(40, 0, "mca: base: component_find: %s", target_file->filename); - - vl = mca_base_component_show_load_errors ? 0 : 40; - - /* Was this component already loaded (e.g., via dependency)? */ - - if (LOADED == target_file->status) { - opal_output_verbose(40, 0, "mca: base: component_find: already loaded (ignored)"); - return OPAL_SUCCESS; - } - - /* Ensure that this component is not already loaded (should only happen - if it was statically loaded). It's an error if it's already - loaded because we're evaluating this file -- not this component. - Hence, returning OPAL_ERR_PARAM indicates that the *file* failed - to load, not the component. */ - - for (cur = opal_list_get_first(found_components); - opal_list_get_end(found_components) != cur; - cur = opal_list_get_next(cur)) { - mitem = (mca_base_component_list_item_t *) cur; - if (0 == strcmp(mitem->cli_component->mca_type_name, target_file->type) && - 0 == strcmp(mitem->cli_component->mca_component_name, target_file->name)) { - opal_output_verbose(40, 0, "mca: base: component_find: already loaded (ignored)"); - target_file->status = FAILED_TO_LOAD; - return OPAL_ERR_BAD_PARAM; - } - } - - /* Look at see if this component has any dependencies. If so, load - them. If we can't load them, then this component must also fail to - load. */ - OBJ_CONSTRUCT(&dependencies, opal_list_t); - if (0 != check_opal_info(target_file, &dependencies, found_components)) { - target_file->status = FAILED_TO_LOAD; - free_dependency_list(&dependencies); - return OPAL_ERR_OUT_OF_RESOURCE; - } - - /* Now try to load the component */ - - char *err_msg; - if (OPAL_SUCCESS != - opal_dl_open(target_file->filename, true, false, &component_handle, - &err_msg)) { - if (NULL != err_msg) { - err_msg = strdup(err_msg); - } else { - err_msg = strdup("opal_dl_open() error message was NULL!"); - } - /* Because libltdl erroneously says "file not found" for any - type of error -- which is especially misleading when the file - is actually there but cannot be opened for some other reason - (e.g., missing symbol) -- do some simple huersitics and if - the file [probably] does exist, print a slightly better error - message. */ - if (0 == strcmp("file not found", err_msg) && - (file_exists(target_file->filename, "lo") || - file_exists(target_file->filename, "so") || - file_exists(target_file->filename, "dylib") || - file_exists(target_file->filename, "dll"))) { - free(err_msg); - err_msg = strdup("perhaps a missing symbol, or compiled for a different version of Open MPI?"); - } - opal_output_verbose(vl, 0, "mca: base: component_find: unable to open %s: %s (ignored)", - target_file->filename, err_msg); - free(err_msg); - target_file->status = FAILED_TO_LOAD; - free_dependency_list(&dependencies); - return OPAL_ERR_BAD_PARAM; - } - - /* Successfully opened the component; now find the public struct. - Malloc out enough space for it. */ - - len = strlen(target_file->type) + strlen(target_file->name) + 32; - struct_name = (char*)malloc(len); - if (NULL == struct_name) { - opal_dl_close(component_handle); - target_file->status = FAILED_TO_LOAD; - free_dependency_list(&dependencies); - return OPAL_ERR_OUT_OF_RESOURCE; - } - snprintf(struct_name, len, "mca_%s_%s_component", target_file->type, - target_file->name); - - mitem = OBJ_NEW(mca_base_component_list_item_t); - if (NULL == mitem) { - free(struct_name); - opal_dl_close(component_handle); - target_file->status = FAILED_TO_LOAD; - free_dependency_list(&dependencies); - return OPAL_ERR_OUT_OF_RESOURCE; - } - - if (OPAL_SUCCESS != opal_dl_lookup(component_handle, struct_name, - (void**) &component_struct, &err_msg) || - NULL == component_struct) { - if (NULL == err_msg) { - err_msg = "opal_dl_loookup() error message was NULL!"; - } - opal_output_verbose(vl, 0, "mca: base: component_find: \"%s\" does not appear to be a valid " - "%s MCA dynamic component (ignored): %s", - target_file->basename, target_file->type, err_msg); - free(mitem); - free(struct_name); - opal_dl_close(component_handle); - target_file->status = FAILED_TO_LOAD; - free_dependency_list(&dependencies); - return OPAL_ERR_BAD_PARAM; - } - - /* We found the public struct. Make sure its MCA major.minor - version is the same as ours. */ - if (!(MCA_BASE_VERSION_MAJOR == component_struct->mca_major_version && - MCA_BASE_VERSION_MINOR == component_struct->mca_minor_version)) { - opal_output_verbose(vl, 0, "mca: base: component_find: %s \"%s\" uses an MCA interface that is not recognized (component MCA v%d.%d.%d != supported MCA v%d.%d.%d) -- ignored", - target_file->type, target_file->basename, - component_struct->mca_major_version, - component_struct->mca_minor_version, - component_struct->mca_release_version, - MCA_BASE_VERSION_MAJOR, - MCA_BASE_VERSION_MINOR, - MCA_BASE_VERSION_RELEASE); - free(mitem); - free(struct_name); - opal_dl_close(component_handle); - target_file->status = FAILED_TO_LOAD; - free_dependency_list(&dependencies); - return OPAL_ERR_BAD_PARAM; - } - - /* Also check that the component struct framework and component - names match the expected names from the filename */ - if (0 != strcmp(component_struct->mca_type_name, target_file->type) || - 0 != strcmp(component_struct->mca_component_name, target_file->name)) { - opal_output_verbose(vl, 0, "Component file data does not match filename: %s (%s / %s) != %s %s -- ignored", - target_file->filename, target_file->type, target_file->name, - component_struct->mca_type_name, - component_struct->mca_component_name); - free(mitem); - free(struct_name); - opal_dl_close(component_handle); - target_file->status = FAILED_TO_LOAD; - free_dependency_list(&dependencies); - return OPAL_ERR_BAD_PARAM; - } - - /* Alles gut. Save the component struct, and register this - component to be closed later. */ - - mitem->cli_component = component_struct; - opal_list_append(found_components, (opal_list_item_t *) mitem); - mca_base_component_repository_retain(target_file->type, component_handle, - component_struct); - - /* Now that that's all done, link all the dependencies in to this - component's repository entry */ - - for (cur = opal_list_remove_first(&dependencies); - NULL != cur; - cur = opal_list_remove_first(&dependencies)) { - ditem = (dependency_item_t *) cur; - mca_base_component_repository_link(target_file->type, - target_file->name, - ditem->di_component_file_item->type, - ditem->di_component_file_item->name); - OBJ_RELEASE(ditem); - } - OBJ_DESTRUCT(&dependencies); - - opal_output_verbose(40, 0, "mca: base: component_find: opened dynamic %s MCA component \"%s\"", - target_file->type, target_file->name); - target_file->status = LOADED; - - /* All done */ - - free(struct_name); - return OPAL_SUCCESS; -} - - -/* - * For a given filename, see if there exists a filename.ompi_info, which - * lists dependencies that must be loaded before this component is - * loaded. If we find this file, try to load those components first. - * - * Detect dependency cycles and error out. - */ -static int check_opal_info(component_file_item_t *target_file, - opal_list_t *dependencies, - opal_list_t *found_components) -{ - size_t len; - FILE *fp; - char *depname; - char buffer[BUFSIZ], *p; - - /* Form the filename */ - - len = strlen(target_file->filename) + strlen(opal_info_suffix) + 16; - depname = (char*)malloc(len); - if (NULL == depname) - return OPAL_ERR_OUT_OF_RESOURCE; - snprintf(depname, len, "%s%s", target_file->filename, opal_info_suffix); - - /* Try to open the file. If there's no file, return success (i.e., - there are no dependencies). */ - - if (NULL == (fp = fopen(depname, "r"))) { - free(depname); - return 0; - } - - /* Otherwise, loop reading the lines in the file and trying to load - them. Return failure upon the first component that fails to - load. */ - - opal_output_verbose(40, 0, "mca: base: component_find: opening .ompi_info file: %s", depname); - while (NULL != fgets(buffer, BUFSIZ, fp)) { - - /* Perl chomp */ - - buffer[BUFSIZ - 1] = '\0'; - len = strlen(buffer); - if ('\n' == buffer[len - 1]) - buffer[len - 1] = '\0'; - - /* Ignore emtpy lines and lines beginning with "#" or "//" */ - - for (p = buffer; '\0' != p; ++p) - if (!isspace(*p)) - break; - - if ('\0' == *p) - continue; - else if (*p == '#' || ('/' == *p && '/' == *(p + 1))) - continue; - - /* Is it a dependency? */ - - else if (0 == strncasecmp(p, key_dependency, strlen(key_dependency))) { - if (OPAL_SUCCESS != check_dependency(p + strlen(key_dependency), - target_file, dependencies, - found_components)) { - fclose(fp); - free(depname); - - /* We can leave any successfully loaded dependencies; we might - need them again later. But free the dependency list for - this component, because since [at least] one of them didn't - load, we have to pretend like all of them didn't load and - disallow loading this component. So free the dependency - list. */ - - free_dependency_list(dependencies); - return OPAL_ERR_OUT_OF_RESOURCE; - } - } - } - opal_output_verbose(40, 0, "mca: base: component_find: ompi_info file closed (%s)", - target_file->basename); - - /* All done -- all depenencies satisfied */ - - fclose(fp); - free(depname); - return 0; -} - - -/* - * A DEPENDENCY key was found in the ompi_info file. Chase it down: see - * if we've already got such a component loaded, or go try to load it if - * it's not already loaded. - */ -static int check_dependency(char *line, component_file_item_t *target_file, - opal_list_t *dependencies, - opal_list_t *found_components) -{ - bool happiness; - char buffer[BUFSIZ]; - char *type, *name; - int len; - component_file_item_t *mitem; - dependency_item_t *ditem; - opal_list_item_t *cur; - - /* Ensure that this was a valid dependency statement */ - - type = line; - name = strchr(line, OPAL_ENV_SEP); - if (NULL == name) { - return OPAL_ERR_OUT_OF_RESOURCE; - } - *name = '\0'; - ++name; - - /* Form the name of the component to compare to */ - - if (strlen(type) + strlen(name) + 32 >= BUFSIZ) { - target_file->status = FAILED_TO_LOAD; - return OPAL_ERR_OUT_OF_RESOURCE; - } - snprintf(buffer, BUFSIZ, component_template, type); - len = strlen(buffer); - strncat(buffer, name, BUFSIZ - len); - - /* Traverse down the list of files that we have, and see if we can - find it */ - - mitem = NULL; - target_file->status = CHECKING_CYCLE; - for (happiness = false, cur = opal_list_get_first(&found_files); - opal_list_get_end(&found_files) != cur; - cur = opal_list_get_next(cur)) { - mitem = (component_file_item_t *) cur; - - /* Compare the name to the basename */ - - if (0 != strcmp(mitem->basename, buffer)) - continue; - - /* Catch the bozo dependency on itself */ - - else if (mitem == target_file) { - opal_output_verbose(40, 0, - "mca: base: component_find: component depends on itself (ignored dependency)"); - happiness = true; - break; - } - - /* If it's loaded, great -- we're done (no need to check that - dependency sub-tree) */ - - else if (LOADED == mitem->status) { - opal_output_verbose(40, 0, "mca: base: component_find: dependency has already been loaded (%s)", - mitem->basename); - happiness = true; - break; - } - - /* If it's specifically not loaded (i.e., there was some kind of - error when we tried to load it), then we cannot meet the - dependencies. */ - - else if (FAILED_TO_LOAD == mitem->status) { - opal_output_verbose(40, 0, "mca: base: component_find: dependency previously failed to load (%s)", - mitem->basename); - break; - } - - /* If we hit a cycle, return badness */ - - else if (CHECKING_CYCLE == mitem->status) { - opal_output_verbose(40, 0, "mca: base: component_find: found cycle! (%s)", - mitem->basename); - break; + ret = mca_base_component_repository_get_components (framework, &dy_components); + if (OPAL_SUCCESS != ret) { + return; } - /* Otherwise, this dependency has not been looked at yet. Go try - to load it. */ - - else if (UNVISITED == mitem->status) { - opal_output_verbose(40, 0, "mca: base: component_find: loading dependency (%s)", - mitem->basename); - if (OPAL_SUCCESS == open_component(target_file, found_components)) { - happiness = true; - } else { - opal_output_verbose(40, 0, "mca: base: component_find: dependency failed to load (%s)", - mitem->basename); - } - break; + /* Iterate through the repository and find components that can be included */ + OPAL_LIST_FOREACH(ri, dy_components, mca_base_component_repository_item_t) { + if (use_component(include_mode, names, ri->ri_name)) { + mca_base_component_repository_open (framework, ri); + } } - } - - /* Did we find the dependency? */ - - if (!happiness) { - target_file->status = FAILED_TO_LOAD; - return OPAL_ERR_BAD_PARAM; - } - - /* The dependency loaded properly. Increment its refcount so that - it doesn't get unloaded before we get unloaded. The (NULL != - mitem) check is somewhat redundant -- we won't be here in this - function unless there's dependencies to check, but a) it's safer - to double check, and b) it fixes a compiler warning. :-) */ - - if (NULL != mitem) { - ditem = OBJ_NEW(dependency_item_t); - if (NULL == ditem) { - return OPAL_ERR_OUT_OF_RESOURCE; - } - ditem->di_component_file_item = mitem; - opal_list_append(dependencies, (opal_list_item_t*) ditem); - } - - /* All done -- all depenencies satisfied */ - - return OPAL_SUCCESS; -} - - -/* - * Free a dependency list - */ -static void free_dependency_list(opal_list_t *dependencies) -{ - opal_list_item_t *item; - - for (item = opal_list_remove_first(dependencies); - NULL != item; - item = opal_list_remove_first(dependencies)) { - OBJ_RELEASE(item); - } - OBJ_DESTRUCT(dependencies); } #endif /* OPAL_HAVE_DL_SUPPORT */ @@ -970,13 +307,16 @@ static bool use_component(const bool include_mode, /* Ensure that *all* requested components exist. Print a warning and abort if they do not. */ -static int component_find_check (const char *framework_name, char **requested_component_names, opal_list_t *components) +static int component_find_check (mca_base_framework_t *framework, char **requested_component_names) { + opal_list_t *components = &framework->framework_components; mca_base_component_list_item_t *cli; - int i; - for (i = 0; NULL != requested_component_names && - NULL != requested_component_names[i]; ++i) { + if (NULL == requested_component_names) { + return OPAL_SUCCESS; + } + + for (int i = 0; NULL != requested_component_names[i]; ++i) { bool found = false; OPAL_LIST_FOREACH(cli, components, mca_base_component_list_item_t) { @@ -992,7 +332,7 @@ static int component_find_check (const char *framework_name, char **requested_co gethostname(h, sizeof(h)); opal_show_help("help-mca-base.txt", "find-available:not-valid", true, - h, framework_name, requested_component_names[i]); + h, framework->framework_name, requested_component_names[i]); return OPAL_ERR_NOT_FOUND; } } diff --git a/opal/mca/base/mca_base_component_repository.c b/opal/mca/base/mca_base_component_repository.c index d04799e01ce..74b9dde59ca 100644 --- a/opal/mca/base/mca_base_component_repository.c +++ b/opal/mca/base/mca_base_component_repository.c @@ -1,3 +1,4 @@ +/* -*- Mode: C; c-basic-offset:4 ; indent-tabs-mode:nil -*- */ /* * Copyright (c) 2004-2005 The Trustees of Indiana University and Indiana * University Research and Technology @@ -10,6 +11,8 @@ * Copyright (c) 2004-2005 The Regents of the University of California. * All rights reserved. * Copyright (c) 2008-2015 Cisco Systems, Inc. All rights reserved. + * Copyright (c) 2015 Los Alamos National Security, LLC. All rights + * reserved. * $COPYRIGHT$ * * Additional copyrights may follow @@ -32,36 +35,18 @@ #include "opal/mca/base/mca_base_component_repository.h" #include "opal/mca/dl/base/base.h" #include "opal/constants.h" +#include "opal/class/opal_hash_table.h" +#include "opal/util/basename.h" #if OPAL_HAVE_DL_SUPPORT /* * Private types */ -struct repository_item_t { - opal_list_item_t super; - - char ri_type[MCA_BASE_MAX_TYPE_NAME_LEN + 1]; - opal_dl_handle_t *ri_dlhandle; - const mca_base_component_t *ri_component_struct; - opal_list_t ri_dependencies; -}; -typedef struct repository_item_t repository_item_t; -static void ri_constructor(opal_object_t *obj); -static void ri_destructor(opal_object_t *obj); -static OBJ_CLASS_INSTANCE(repository_item_t, opal_list_item_t, - ri_constructor, ri_destructor); - -struct dependency_item_t { - opal_list_item_t super; - - repository_item_t *di_repository_entry; -}; -typedef struct dependency_item_t dependency_item_t; -static void di_constructor(opal_object_t *obj); -static void di_destructor(opal_object_t *obj); -static OBJ_CLASS_INSTANCE(dependency_item_t, opal_list_item_t, - di_constructor, di_destructor); +static void ri_constructor(mca_base_component_repository_item_t *ri); +static void ri_destructor(mca_base_component_repository_item_t *ri); +OBJ_CLASS_INSTANCE(mca_base_component_repository_item_t, opal_list_item_t, + ri_constructor, ri_destructor); #endif /* OPAL_HAVE_DL_SUPPORT */ @@ -74,321 +59,468 @@ static bool initialized = false; #if OPAL_HAVE_DL_SUPPORT -static opal_list_t repository; - - -/* - * Private functions - */ -static repository_item_t *find_component(const char *type, const char *name); -static int link_items(repository_item_t *src, repository_item_t *depend); +static opal_hash_table_t mca_base_component_repository; -#endif /* OPAL_HAVE_DL_SUPPORT */ +/* two-level macro for stringifying a number */ +#define STRINGIFYX(x) #x +#define STRINGIFY(x) STRINGIFYX(x) - -/* - * Initialize the repository - */ -int mca_base_component_repository_init(void) +static int process_repository_item (const char *filename, void *data) { - /* Setup internal structures */ - - if (!initialized) { -#if OPAL_HAVE_DL_SUPPORT + char name[MCA_BASE_MAX_COMPONENT_NAME_LEN + 1]; + char type[MCA_BASE_MAX_TYPE_NAME_LEN + 1]; + mca_base_component_repository_item_t *ri; + opal_list_t *component_list; + char *base; + int ret; + + base = opal_basename (filename); + if (NULL == base) { + return OPAL_ERROR; + } - /* Initialize the dl framework */ - int ret = mca_base_framework_open(&opal_dl_base_framework, 0); - if (OPAL_SUCCESS != ret) { - opal_output(0, "%s %d:%s failed -- process will likely abort (open the dl framework returned %d instead of OPAL_SUCCESS)\n", - __FILE__, __LINE__, __func__, ret); - return ret; + /* check if the plugin has the appropriate prefix */ + if (0 != strncmp (base, "mca_", 4)) { + free (base); + return OPAL_SUCCESS; } - opal_dl_base_select(); - OBJ_CONSTRUCT(&repository, opal_list_t); -#endif + /* read framework and component names. framework names may not include an _ + * but component names may */ + ret = sscanf (base, "mca_%" STRINGIFY(MCA_BASE_MAX_TYPE_NAME_LEN) "[^_]_%" + STRINGIFY(MCA_BASE_MAX_COMPONENT_NAME_LEN) "s", type, name); + if (0 > ret) { + /* does not patch the expected template. skip */ + return OPAL_SUCCESS; + } - initialized = true; - } + /* lookup the associated framework list and create if it doesn't already exist */ + ret = opal_hash_table_get_value_ptr (&mca_base_component_repository, type, + strlen (type), (void **) &component_list); + if (OPAL_SUCCESS != ret) { + component_list = OBJ_NEW(opal_list_t); + if (NULL == component_list) { + free (base); + /* OOM. nothing to do but fail */ + return OPAL_ERR_OUT_OF_RESOURCE; + } + + ret = opal_hash_table_set_value_ptr (&mca_base_component_repository, type, + strlen (type), (void *) component_list); + if (OPAL_SUCCESS != ret) { + free (base); + OBJ_RELEASE(component_list); + return ret; + } + } - /* All done */ + /* check for duplicate components */ + OPAL_LIST_FOREACH(ri, component_list, mca_base_component_repository_item_t) { + if (0 == strcmp (ri->ri_name, name)) { + /* already scanned this component */ + free (base); + return OPAL_SUCCESS; + } + } - return OPAL_SUCCESS; -} + ri = OBJ_NEW(mca_base_component_repository_item_t); + if (NULL == ri) { + return OPAL_ERR_OUT_OF_RESOURCE; + } + ri->ri_base = base; -/* - * Add a newly-opened dyanmic component to the repository of open - * components. The component's type, handle, and public struct are - * saved. - */ -int mca_base_component_repository_retain(char *type, - opal_dl_handle_t *component_handle, - const mca_base_component_t *component_struct) -{ -#if OPAL_HAVE_DL_SUPPORT - repository_item_t *ri; + ri->ri_path = strdup (filename); + if (NULL == ri->ri_path) { + OBJ_RELEASE(ri); + return OPAL_ERR_OUT_OF_RESOURCE; + } - /* Allocate a new repository item */ + /* strncpy does not guarantee a \0 */ + ri->ri_type[MCA_BASE_MAX_TYPE_NAME_LEN] = '\0'; + strncpy (ri->ri_type, type, MCA_BASE_MAX_TYPE_NAME_LEN); - ri = OBJ_NEW(repository_item_t); - if (NULL == ri) { - return OPAL_ERR_OUT_OF_RESOURCE; - } + ri->ri_name[MCA_BASE_MAX_TYPE_NAME_LEN] = '\0'; + strncpy (ri->ri_name, name, MCA_BASE_MAX_COMPONENT_NAME_LEN); - /* Initialize the repository item */ + opal_list_append (component_list, &ri->super); - strncpy(ri->ri_type, type, MCA_BASE_MAX_TYPE_NAME_LEN); - ri->ri_type[MCA_BASE_MAX_TYPE_NAME_LEN] = '\0'; - ri->ri_dlhandle = component_handle; - ri->ri_component_struct = component_struct; + return OPAL_SUCCESS; +} - /* Append the new item to the repository */ +static int file_exists(const char *filename, const char *ext) +{ + char *final; + int ret; - opal_list_append(&repository, (opal_list_item_t *) ri); + if (NULL == ext) { + return access (filename, F_OK) == 0; + } - /* All done */ + ret = asprintf(&final, "%s.%s", filename, ext); + if (0 > ret || NULL == final) { + return 0; + } - return OPAL_SUCCESS; -#else - return OPAL_ERR_NOT_SUPPORTED; -#endif + ret = access (final, F_OK); + free(final); + return (0 == ret); } +#endif /* OPAL_HAVE_DL_SUPPORT */ -/* - * Bump up the refcount on a component - */ -int mca_base_component_repository_retain_component(const char *type, - const char *name) +int mca_base_component_repository_add (const char *path) { #if OPAL_HAVE_DL_SUPPORT - repository_item_t *ri = find_component(type, name); - if (NULL != ri) { - OBJ_RETAIN(ri); + char *path_to_use = NULL, *dir, *ctx; + const char sep[] = {OPAL_ENV_SEP, '\0'}; + + if (NULL == path) { + /* nothing to do */ return OPAL_SUCCESS; } - return OPAL_ERR_NOT_FOUND; -#else - return OPAL_ERR_NOT_SUPPORTED; -#endif -} + path_to_use = strdup (path); -/* - * Create a dependency from one component entry to another - */ -int mca_base_component_repository_link(const char *src_type, - const char *src_name, - const char *depend_type, - const char *depend_name) -{ -#if OPAL_HAVE_DL_SUPPORT - repository_item_t *src, *depend; - - /* Look up the two components */ - - src = find_component(src_type, src_name); - if (NULL == src) { - return OPAL_ERR_BAD_PARAM; - } - depend = find_component(depend_type, depend_name); - if (NULL == depend) { - return OPAL_ERR_BAD_PARAM; - } + dir = strtok_r (path_to_use, sep, &ctx); + do { + if ((0 == strcmp(dir, "USER_DEFAULT") || 0 == strcmp(dir, "USR_DEFAULT")) + && NULL != mca_base_user_default_path) { + dir = mca_base_user_default_path; + } else if (0 == strcmp(dir, "SYS_DEFAULT") || + 0 == strcmp(dir, "SYSTEM_DEFAULT")) { + dir = mca_base_system_default_path; + } + + if (0 != opal_dl_foreachfile(dir, process_repository_item, NULL)) { + break; + } + } while (NULL != (dir = strtok_r (NULL, sep, &ctx))); - /* Link them */ +#endif /* OPAL_HAVE_DL_SUPPORT */ - return link_items(src, depend); -#else - return OPAL_ERR_NOT_SUPPORTED; -#endif + return OPAL_SUCCESS; } /* - * If it's in the repository, close a specified component and remove - * it from the repository. + * Initialize the repository */ -void mca_base_component_repository_release(const mca_base_component_t *component) +int mca_base_component_repository_init(void) { + /* Setup internal structures */ + + if (!initialized) { #if OPAL_HAVE_DL_SUPPORT - if (initialized) { - repository_item_t *ri = find_component(component->mca_type_name, - component->mca_component_name); - if (NULL != ri) { - OBJ_RELEASE(ri); + + /* Initialize the dl framework */ + int ret = mca_base_framework_open(&opal_dl_base_framework, 0); + if (OPAL_SUCCESS != ret) { + opal_output(0, "%s %d:%s failed -- process will likely abort (open the dl framework returned %d instead of OPAL_SUCCESS)\n", + __FILE__, __LINE__, __func__, ret); + return ret; } - } -#endif -} + opal_dl_base_select(); + OBJ_CONSTRUCT(&mca_base_component_repository, opal_hash_table_t); + ret = opal_hash_table_init (&mca_base_component_repository, 128); + if (OPAL_SUCCESS != ret) { + mca_base_framework_close (&opal_dl_base_framework); + return ret; + } -/* - * Finalize the repository -- close everything that's still open. - */ -void mca_base_component_repository_finalize(void) -{ -#if OPAL_HAVE_DL_SUPPORT - repository_item_t *ri, *next; + ret = mca_base_component_repository_add (mca_base_component_path); + if (OPAL_SUCCESS != ret) { + OBJ_DESTRUCT(&mca_base_component_repository); + mca_base_framework_close (&opal_dl_base_framework); + return ret; + } #endif - if (initialized) { -#if OPAL_HAVE_DL_SUPPORT - - /* Have to be slightly careful about this because of dependencies, - particularly on OS's where it matters (i.e., closing a - component that is depended on by other components actually - causes missing symbols because the OS actually does unload it - from memory!), such as OS X. + initialized = true; + } - So instead of just blindly closing everything, we have iterate - over the array of open components releasing everything with a - refcount of 1 -- skip anything with a refcount of more than 1. - Repeat this procedure until either we have nothing open or we - made one full pass and no refcounts went to 1 (which is - technically an error). */ + /* All done */ - do { - OPAL_LIST_FOREACH_SAFE(ri, next, &repository, repository_item_t) { - OBJ_RELEASE(ri); - } - } while (opal_list_get_size(&repository) > 0); + return OPAL_SUCCESS; +} - (void) mca_base_framework_close(&opal_dl_base_framework); +int mca_base_component_repository_get_components (mca_base_framework_t *framework, + opal_list_t **framework_components) +{ + *framework_components = NULL; +#if OPAL_HAVE_DL_SUPPORT + return opal_hash_table_get_value_ptr (&mca_base_component_repository, framework->framework_name, + strlen (framework->framework_name), (void **) framework_components); #endif - - initialized = false; - } + return OPAL_ERR_NOT_FOUND; } -#if OPAL_HAVE_DL_SUPPORT +static void mca_base_component_repository_release_internal (mca_base_component_repository_item_t *ri) { + int group_id; -static repository_item_t *find_component(const char *type, const char *name) -{ - opal_list_item_t *item; - repository_item_t *ri; - - for (item = opal_list_get_first(&repository); - opal_list_get_end(&repository) != item; - item = opal_list_get_next(item)) { - ri = (repository_item_t *) item; - if (0 == strcmp(ri->ri_type, type) && - 0 == strcmp(ri->ri_component_struct->mca_component_name, name)) { - return ri; + group_id = mca_base_var_group_find (NULL, ri->ri_type, ri->ri_name); + if (0 <= group_id) { + /* ensure all variables are deregistered before we dlclose the component */ + mca_base_var_group_deregister (group_id); } - } - - /* Not found */ - return NULL; + /* Close the component (and potentially unload it from memory */ + if (ri->ri_dlhandle) { + opal_dl_close(ri->ri_dlhandle); + ri->ri_dlhandle = NULL; + } } - -static int link_items(repository_item_t *src, repository_item_t *depend) +void mca_base_component_repository_release(const mca_base_component_t *component) { - dependency_item_t *di; + mca_base_component_repository_item_t *ri; + opal_list_t *component_list; + int ret; - /* Bozo check */ + ret = opal_hash_table_get_value_ptr (&mca_base_component_repository, component->mca_type_name, + strlen (component->mca_type_name), (void **) &component_list); + if (OPAL_SUCCESS != ret) { + /* component does not exist in the repository */ + return; + } - if (NULL == src || NULL == depend) { - return OPAL_ERR_BAD_PARAM; - } + OPAL_LIST_FOREACH(ri, component_list, mca_base_component_repository_item_t) { + if (0 == strcmp (ri->ri_name, component->mca_component_name)) { + /* go ahead and dlclose the component if it is open */ + mca_base_component_repository_release_internal (ri); + break; + } + } +} - /* Make a new depedency item */ - di = OBJ_NEW(dependency_item_t); - if (NULL == di) { - return OPAL_ERR_OUT_OF_RESOURCE; - } +int mca_base_component_repository_open (mca_base_framework_t *framework, + mca_base_component_repository_item_t *ri) +{ +#if OPAL_HAVE_DL_SUPPORT + mca_base_component_t *component_struct; + mca_base_component_list_item_t *mitem = NULL; + char *struct_name = NULL; + int vl, ret; + + opal_output_verbose(40, 0, "mca_base_component_repository_open: examining dynamic %s MCA component \"%s\" at path %s", + ri->ri_type, ri->ri_name, ri->ri_path); + + vl = mca_base_component_show_load_errors ? 0 : 40; + + /* Ensure that this component is not already loaded (should only happen + if it was statically loaded). It's an error if it's already + loaded because we're evaluating this file -- not this component. + Hence, returning OPAL_ERR_PARAM indicates that the *file* failed + to load, not the component. */ + + OPAL_LIST_FOREACH(mitem, &framework->framework_components, mca_base_component_list_item_t) { + if (0 == strcmp(mitem->cli_component->mca_component_name, ri->ri_name)) { + opal_output_verbose(40, 0, "mca_base_component_repository_open: already loaded (ignored)"); + return OPAL_ERR_BAD_PARAM; + } + } - /* Initialize the new dependency item */ + if (NULL != ri->ri_dlhandle) { + opal_output_verbose(40, 0, "mca_base_component_repository_open: already loaded. returning cached component"); + mitem = OBJ_NEW(mca_base_component_list_item_t); + if (NULL == mitem) { + return OPAL_ERR_OUT_OF_RESOURCE; + } - di->di_repository_entry = depend; + mitem->cli_component = ri->ri_component_struct; + opal_list_append (&framework->framework_components, &mitem->super); - /* Add it to the dependency list on the source repository entry */ + return OPAL_SUCCESS; + } - opal_list_append(&src->ri_dependencies, (opal_list_item_t *) di); + if (0 != strcmp (ri->ri_type, framework->framework_name)) { + /* shouldn't happen. attempting to open a component belonging to + * another framework. if this happens it is likely a MCA base + * bug so assert */ + assert (0); + return OPAL_ERR_NOT_SUPPORTED; + } - /* Increment the refcount in the dependency */ + /* Now try to load the component */ + + char *err_msg = NULL; + if (OPAL_SUCCESS != opal_dl_open(ri->ri_path, true, false, &ri->ri_dlhandle, &err_msg)) { + if (NULL == err_msg) { + err_msg = "opal_dl_open() error message was NULL!"; + } + /* Because libltdl erroneously says "file not found" for any + type of error -- which is especially misleading when the file + is actually there but cannot be opened for some other reason + (e.g., missing symbol) -- do some simple huersitics and if + the file [probably] does exist, print a slightly better error + message. */ + if (0 == strcasecmp("file not found", err_msg) && + (file_exists(ri->ri_path, "lo") || + file_exists(ri->ri_path, "so") || + file_exists(ri->ri_path, "dylib") || + file_exists(ri->ri_path, "dll"))) { + err_msg = "perhaps a missing symbol, or compiled for a different version of Open MPI?"; + } + opal_output_verbose(vl, 0, "mca_base_component_repository_open: unable to open %s: %s (ignored)", + ri->ri_base, err_msg); + return OPAL_ERR_BAD_PARAM; + } - OBJ_RETAIN(depend); + /* Successfully opened the component; now find the public struct. + Malloc out enough space for it. */ - /* All done */ + do { + ret = asprintf (&struct_name, "mca_%s_%s_component", ri->ri_type, ri->ri_name); + if (0 > ret) { + ret = OPAL_ERR_OUT_OF_RESOURCE; + break; + } + + mitem = OBJ_NEW(mca_base_component_list_item_t); + if (NULL == mitem) { + ret = OPAL_ERR_OUT_OF_RESOURCE; + break; + } + + err_msg = NULL; + ret = opal_dl_lookup(ri->ri_dlhandle, struct_name, (void**) &component_struct, &err_msg); + if (OPAL_SUCCESS != ret || NULL == component_struct) { + if (NULL == err_msg) { + err_msg = "opal_dl_loookup() error message was NULL!"; + } + opal_output_verbose(vl, 0, "mca_base_component_repository_open: \"%s\" does not appear to be a valid " + "%s MCA dynamic component (ignored): %s. ret %d", ri->ri_base, ri->ri_type, err_msg, ret); + + ret = OPAL_ERR_BAD_PARAM; + break; + } + + /* done with the structure name */ + free (struct_name); + + /* We found the public struct. Make sure its MCA major.minor + version is the same as ours. TODO -- add checks for project version (from framework) */ + if (!(MCA_BASE_VERSION_MAJOR == component_struct->mca_major_version && + MCA_BASE_VERSION_MINOR == component_struct->mca_minor_version)) { + opal_output_verbose(vl, 0, "mca_base_component_repository_open: %s \"%s\" uses an MCA interface that is " + "not recognized (component MCA v%d.%d.%d != supported MCA v%d.%d.%d) -- ignored", + ri->ri_type, ri->ri_path, component_struct->mca_major_version, + component_struct->mca_minor_version, component_struct->mca_release_version, + MCA_BASE_VERSION_MAJOR, MCA_BASE_VERSION_MINOR, MCA_BASE_VERSION_RELEASE); + ret = OPAL_ERR_BAD_PARAM; + break; + } + + /* Also check that the component struct framework and component + names match the expected names from the filename */ + if (0 != strcmp(component_struct->mca_type_name, ri->ri_type) || + 0 != strcmp(component_struct->mca_component_name, ri->ri_name)) { + opal_output_verbose(vl, 0, "Component file data does not match filename: %s (%s / %s) != %s %s -- ignored", + ri->ri_path, ri->ri_type, ri->ri_name, + component_struct->mca_type_name, + component_struct->mca_component_name); + ret = OPAL_ERR_BAD_PARAM; + break; + } + + /* Alles gut. Save the component struct, and register this + component to be closed later. */ + + ri->ri_component_struct = mitem->cli_component = component_struct; + opal_list_append(&framework->framework_components, &mitem->super); + + opal_output_verbose(40, 0, "mca_base_component_repository_open: opened dynamic %s MCA component \"%s\"", + ri->ri_type, ri->ri_name); - return OPAL_SUCCESS; -} + return OPAL_SUCCESS; + } while (0); + if (mitem) { + OBJ_RELEASE(mitem); + } -/* - * Basic sentinel values, and construct the inner list - */ -static void ri_constructor(opal_object_t *obj) -{ - repository_item_t *ri = (repository_item_t *) obj; + if (struct_name) { + free (struct_name); + } - memset(ri->ri_type, 0, sizeof(ri->ri_type)); - ri->ri_dlhandle = NULL; - ri->ri_component_struct = NULL; + opal_dl_close (ri->ri_dlhandle); + ri->ri_dlhandle = NULL; - OBJ_CONSTRUCT(&ri->ri_dependencies, opal_list_t); -} + return ret; +#else + /* no dlopen support */ + return OPAL_ERR_NOT_SUPPORTED; +#endif +} /* - * Close a component + * Finalize the repository -- close everything that's still open. */ -static void ri_destructor(opal_object_t *obj) +void mca_base_component_repository_finalize(void) { - repository_item_t *ri = (repository_item_t *) obj; - opal_list_item_t *item; - int group_id; - - group_id = mca_base_var_group_find (NULL, ri->ri_type, - ri->ri_component_struct->mca_component_name); - if (0 <= group_id) { - mca_base_var_group_deregister (group_id); - } - - /* Close the component (and potentially unload it from memory */ - opal_dl_close(ri->ri_dlhandle); + if (!initialized) { + return; + } - /* It should be obvious, but I'll state it anyway because it bit me - during debugging: after the dlclose(), the mca_base_component_t - pointer is no longer valid because it has [potentially] been - unloaded from memory. So don't try to use it. :-) */ + initialized = false; - /* Now go release/close (at a minimum: decrement the refcount) any - dependencies of this component */ +#if OPAL_HAVE_DL_SUPPORT + opal_list_t *component_list; + void *node, *key; + size_t key_size; + int ret; + + ret = opal_hash_table_get_first_key_ptr (&mca_base_component_repository, &key, &key_size, + (void **) &component_list, &node); + while (OPAL_SUCCESS == ret) { + OPAL_LIST_RELEASE(component_list); + ret = opal_hash_table_get_next_key_ptr (&mca_base_component_repository, &key, + &key_size, (void **) &component_list, + node, &node); + } - while (NULL != (item = opal_list_remove_first(&ri->ri_dependencies))) { - OBJ_RELEASE(item); - } - OBJ_DESTRUCT(&ri->ri_dependencies); - opal_list_remove_item(&repository, (opal_list_item_t *) ri); + (void) mca_base_framework_close(&opal_dl_base_framework); + OBJ_DESTRUCT(&mca_base_component_repository); +#endif } +#if OPAL_HAVE_DL_SUPPORT /* - * Basic sentinel values + * Basic sentinel values, and construct the inner list */ -static void di_constructor(opal_object_t *obj) +static void ri_constructor (mca_base_component_repository_item_t *ri) { - dependency_item_t *di = (dependency_item_t *) obj; - - di->di_repository_entry = NULL; + memset(ri->ri_type, 0, sizeof(ri->ri_type)); + ri->ri_dlhandle = NULL; + ri->ri_component_struct = NULL; + ri->ri_path = NULL; } /* - * When a dependency item is released, go release the repository entry - * that it points to + * Close a component */ -static void di_destructor(opal_object_t *obj) +static void ri_destructor (mca_base_component_repository_item_t *ri) { - dependency_item_t *di = (dependency_item_t *) obj; + /* dlclose the component if it is still open */ + mca_base_component_repository_release_internal (ri); + + /* It should be obvious, but I'll state it anyway because it bit me + during debugging: after the dlclose(), the mca_base_component_t + pointer is no longer valid because it has [potentially] been + unloaded from memory. So don't try to use it. :-) */ - OBJ_RELEASE(di->di_repository_entry); + if (ri->ri_path) { + free (ri->ri_path); + } + + if (ri->ri_base) { + free (ri->ri_base); + } } #endif /* OPAL_HAVE_DL_SUPPORT */ diff --git a/opal/mca/base/mca_base_component_repository.h b/opal/mca/base/mca_base_component_repository.h index fba02334207..8ba27f1da93 100644 --- a/opal/mca/base/mca_base_component_repository.h +++ b/opal/mca/base/mca_base_component_repository.h @@ -17,6 +17,19 @@ * $HEADER$ */ +/** + * @file mca_base_component_repository.h + * + * This file provide the external interface to our base component + * module. Most of the components that depend on it, will use the + * retain_component() function to increase the reference count on a + * particular component (as opposed to the retain() function, which is + * internal to the opal/mca/base). But it's convenient to have all + * the functions exported from one header file rather than to separate + * retain_component() and retain() into two separate header files + * (i.e., have a separate header file just for retain()). + */ + #ifndef MCA_BASE_COMPONENT_REPOSITORY_H #define MCA_BASE_COMPONENT_REPOSITORY_H @@ -26,31 +39,71 @@ #include "opal/mca/dl/base/base.h" BEGIN_C_DECLS +struct mca_base_component_repository_item_t { + opal_list_item_t super; - OPAL_DECLSPEC int mca_base_component_repository_init(void); + char ri_type[MCA_BASE_MAX_TYPE_NAME_LEN + 1]; + char ri_name[MCA_BASE_MAX_COMPONENT_NAME_LEN + 1]; -/* This file provide the external interface to our base component - * module. Most of the components that depend on it, will use the - * retain_component() function to increase the reference count on a - * particular component (as opposed to the retain() function, which is - * internal to the opal/mca/base). But it's convenient to have all - * the functions exported from one header file rather than to separate - * retain_component() and retain() into two separate header files - * (i.e., have a separate header file just for retain()). + char *ri_path; + char *ri_base; + + opal_dl_handle_t *ri_dlhandle; + const mca_base_component_t *ri_component_struct; +}; +typedef struct mca_base_component_repository_item_t mca_base_component_repository_item_t; + +OBJ_CLASS_DECLARATION(mca_base_component_repository_item_t); + +/** + * @brief initialize the component repository + * + * This function must be called before any frameworks are registered or + * opened. It is responsible for setting up the repository of dynamically + * loaded components. The initial search path is taken from the + * mca_base_component_path MCA parameter. mca_base_open () is a + * prerequisite call as it registers the mca_base_component_path parameter. + */ +OPAL_DECLSPEC int mca_base_component_repository_init(void); + +/** + * @brief add search path for dynamically loaded components + * + * @param[in] path delimited list of search paths to add + */ +OPAL_DECLSPEC int mca_base_component_repository_add (const char *path); + + +/** + * @brief return the list of components that match a given framework + * + * @param[in] framework framework to match + * @param[out] framework_components components that match this framework + * + * The list returned in {framework_components} is owned by the component + * repository and CAN NOT be modified by the caller. */ - OPAL_DECLSPEC int mca_base_component_repository_retain(char *type, - opal_dl_handle_t *component_handle, - const mca_base_component_t *component_struct); - - OPAL_DECLSPEC int mca_base_component_repository_retain_component(const char *type, - const char *name); - OPAL_DECLSPEC int mca_base_component_repository_link(const char *src_type, - const char *src_name, - const char *depend_type, - const char *depend_name); - OPAL_DECLSPEC void mca_base_component_repository_release(const mca_base_component_t *component); - OPAL_DECLSPEC void mca_base_component_repository_finalize(void); - +OPAL_DECLSPEC int mca_base_component_repository_get_components (mca_base_framework_t *framework, + opal_list_t **framework_components); + +/** + * @brief finalize the mca component repository + */ +OPAL_DECLSPEC void mca_base_component_repository_finalize(void); + +/** + * @brief open the repository item and add it to the framework's component + * list + * + * @param[in] framework framework that matches the component + * @param[in] ri dynamic component to open + */ +int mca_base_component_repository_open (mca_base_framework_t *framework, + mca_base_component_repository_item_t *ri); + + +void mca_base_component_repository_release(const mca_base_component_t *component); + END_C_DECLS #endif /* MCA_BASE_COMPONENT_REPOSITORY_H */ diff --git a/opal/mca/base/mca_base_components_close.c b/opal/mca/base/mca_base_components_close.c index 294b4e5e742..232b0a38b34 100644 --- a/opal/mca/base/mca_base_components_close.c +++ b/opal/mca/base/mca_base_components_close.c @@ -44,7 +44,7 @@ void mca_base_component_unload (const mca_base_component_t *component, int outpu mca_base_var_group_deregister (ret); } - mca_base_component_repository_release((mca_base_component_t *) component); + mca_base_component_repository_release (component); } void mca_base_component_close (const mca_base_component_t *component, int output_id) diff --git a/opal/mca/base/mca_base_components_open.c b/opal/mca/base/mca_base_components_open.c index 59a1e7b4514..76475333003 100644 --- a/opal/mca/base/mca_base_components_open.c +++ b/opal/mca/base/mca_base_components_open.c @@ -11,7 +11,7 @@ * Copyright (c) 2004-2005 The Regents of the University of California. * All rights reserved. * Copyright (c) 2008-2012 Cisco Systems, Inc. All rights reserved. - * Copyright (c) 2011-2013 Los Alamos National Security, LLC. + * Copyright (c) 2011-2015 Los Alamos National Security, LLC. * All rights reserved. * Copyright (c) 2014 Hochschule Esslingen. All rights reserved. * $COPYRIGHT$ @@ -56,11 +56,9 @@ int mca_base_framework_components_open (mca_base_framework_t *framework, { /* Open flags are not used at this time. Suppress compiler warning. */ if (flags & MCA_BASE_OPEN_FIND_COMPONENTS) { + bool open_dso_components = !(flags & MCA_BASE_OPEN_STATIC_ONLY); /* Find and load requested components */ - int ret = mca_base_component_find(NULL, framework->framework_name, - framework->framework_static_components, - framework->framework_selection, - &framework->framework_components, true); + int ret = mca_base_component_find(NULL, framework, false, open_dso_components); if (OPAL_SUCCESS != ret) { return ret; } @@ -70,53 +68,6 @@ int mca_base_framework_components_open (mca_base_framework_t *framework, return open_components (framework); } -int mca_base_components_open (const char *type_name, int output_id, - const mca_base_component_t **static_components, - opal_list_t *components_available, - bool open_dso_components) -{ - /* create a dummy framework -- this leaks -- i know -- but it is temporary */ - mca_base_register_flag_t register_flags; - mca_base_framework_t *dummy_framework; - opal_list_item_t *item; - int ret; - - dummy_framework = calloc (1, sizeof(*dummy_framework)); - - dummy_framework->framework_static_components = static_components; - dummy_framework->framework_output = output_id; - dummy_framework->framework_name = strdup(type_name); - - if (open_dso_components) { - register_flags = MCA_BASE_REGISTER_STATIC_ONLY; - } else { - register_flags = MCA_BASE_REGISTER_DEFAULT; - } - - ret = mca_base_framework_components_register (dummy_framework, register_flags); - if (OPAL_SUCCESS != ret) { - free (dummy_framework); - return ret; - } - - ret = mca_base_framework_components_open (dummy_framework, 0); - if (OPAL_SUCCESS != ret) { - (void) mca_base_framework_components_close (dummy_framework, NULL); - free (dummy_framework); - return ret; - } - - OBJ_CONSTRUCT(components_available, opal_list_t); - - while (NULL != (item = opal_list_remove_first(&dummy_framework->framework_components))) { - opal_list_append(components_available, item); - } - - OBJ_DESTRUCT(&dummy_framework->framework_components); - - return OPAL_SUCCESS; -} - /* * Traverse the entire list of found components (a list of * mca_base_component_t instances). If the requested_component_names @@ -152,16 +103,13 @@ static int open_components(mca_base_framework_t *framework) /* If mca_base_framework_register_components was called with the MCA_BASE_COMPONENTS_ALL flag we need to trim down and close any extra components we do not want open */ - ret = mca_base_components_filter (framework->framework_name, &framework->framework_components, - framework->framework_output, framework->framework_selection, - open_only_flags); + ret = mca_base_components_filter (framework, open_only_flags); if (OPAL_SUCCESS != ret) { return ret; } /* Announce */ - opal_output_verbose(10, output_id, - "mca: base: components_open: opening %s components", + opal_output_verbose(10, output_id, "mca: base: components_open: opening %s components", framework->framework_name); /* Traverse the list of components */ diff --git a/opal/mca/base/mca_base_components_register.c b/opal/mca/base/mca_base_components_register.c index 7c7743ac22c..54a06b29660 100644 --- a/opal/mca/base/mca_base_components_register.c +++ b/opal/mca/base/mca_base_components_register.c @@ -11,7 +11,7 @@ * Copyright (c) 2004-2005 The Regents of the University of California. * All rights reserved. * Copyright (c) 2008-2012 Cisco Systems, Inc. All rights reserved. - * Copyright (c) 2011-2013 Los Alamos National Security, LLC. + * Copyright (c) 2011-2015 Los Alamos National Security, LLC. * All rights reserved. * $COPYRIGHT$ * @@ -39,8 +39,7 @@ /* * Local functions */ -static int register_components(const char *project_name, const char *type_name, - int output_id, opal_list_t *src, opal_list_t *dest); +static int register_components(mca_base_framework_t *framework); /** * Function for finding and opening either all MCA components, or the * one that was specifically requested via a MCA parameter. @@ -50,28 +49,16 @@ int mca_base_framework_components_register (mca_base_framework_t *framework, { bool open_dso_components = !(flags & MCA_BASE_REGISTER_STATIC_ONLY); bool ignore_requested = !!(flags & MCA_BASE_REGISTER_ALL); - opal_list_t components_found; int ret; /* Find and load requested components */ - ret = mca_base_component_find(NULL, framework->framework_name, - framework->framework_static_components, - ignore_requested ? NULL : framework->framework_selection, - &components_found, open_dso_components); - + ret = mca_base_component_find(NULL, framework, ignore_requested, open_dso_components); if (OPAL_SUCCESS != ret) { return ret; } /* Register all remaining components */ - ret = register_components(framework->framework_project, framework->framework_name, - framework->framework_output, &components_found, - &framework->framework_components); - - OBJ_DESTRUCT(&components_found); - - /* All done */ - return ret; + return register_components(framework); } /* @@ -81,24 +68,21 @@ int mca_base_framework_components_register (mca_base_framework_t *framework, * components is in the requested_components_array, try to open it. * If it opens, add it to the components_available list. */ -static int register_components(const char *project_name, const char *type_name, - int output_id, opal_list_t *src, opal_list_t *dest) +static int register_components(mca_base_framework_t *framework) { int ret; - opal_list_item_t *item; mca_base_component_t *component; - mca_base_component_list_item_t *cli; + mca_base_component_list_item_t *cli, *next; + int output_id = framework->framework_output; /* Announce */ opal_output_verbose(10, output_id, - "mca: base: components_register: registering %s components", - type_name); + "mca: base: components_register: registering framework %s components", + framework->framework_name); /* Traverse the list of found components */ - OBJ_CONSTRUCT(dest, opal_list_t); - while (NULL != (item = opal_list_remove_first (src))) { - cli = (mca_base_component_list_item_t *) item; + OPAL_LIST_FOREACH_SAFE(cli, next, &framework->framework_components, mca_base_component_list_item_t) { component = (mca_base_component_t *)cli->cli_component; opal_output_verbose(10, output_id, @@ -142,7 +126,7 @@ static int register_components(const char *project_name, const char *type_name, component->mca_component_name); } - mca_base_component_unload (component, output_id); + opal_list_remove_item (&framework->framework_components, &cli->super); /* Release this list item */ OBJ_RELEASE(cli); @@ -168,8 +152,6 @@ static int register_components(const char *project_name, const char *type_name, 0, MCA_BASE_VAR_FLAG_DEFAULT_ONLY | MCA_BASE_VAR_FLAG_INTERNAL, OPAL_INFO_LVL_9, MCA_BASE_VAR_SCOPE_CONSTANT, &component->mca_component_release_version); - - opal_list_append(dest, item); } /* All done */ diff --git a/opal/mca/base/mca_base_framework.c b/opal/mca/base/mca_base_framework.c index 18c55517fc1..039e7421e44 100644 --- a/opal/mca/base/mca_base_framework.c +++ b/opal/mca/base/mca_base_framework.c @@ -65,6 +65,8 @@ int mca_base_framework_register (struct mca_base_framework_t *framework, return OPAL_SUCCESS; } + OBJ_CONSTRUCT(&framework->framework_components, opal_list_t); + if (framework->framework_flags & MCA_BASE_FRAMEWORK_FLAG_NO_DSO) { flags |= MCA_BASE_REGISTER_STATIC_ONLY; } @@ -147,6 +149,10 @@ int mca_base_framework_open (struct mca_base_framework_t *framework, if (MCA_BASE_FRAMEWORK_FLAG_NOREGISTER & framework->framework_flags) { flags |= MCA_BASE_OPEN_FIND_COMPONENTS; + + if (MCA_BASE_FRAMEWORK_FLAG_NO_DSO & framework->framework_flags) { + flags |= MCA_BASE_OPEN_STATIC_ONLY; + } } /* lock all of this frameworks's variables */ @@ -221,6 +227,8 @@ int mca_base_framework_close (struct mca_base_framework_t *framework) { framework->framework_flags &= ~(MCA_BASE_FRAMEWORK_FLAG_REGISTERED | MCA_BASE_FRAMEWORK_FLAG_OPEN); + OBJ_DESTRUCT(&framework->framework_components); + framework_close_output (framework); return ret; diff --git a/opal/mca/base/mca_base_framework.h b/opal/mca/base/mca_base_framework.h index bfc78ec12ff..43aa36a731c 100644 --- a/opal/mca/base/mca_base_framework.h +++ b/opal/mca/base/mca_base_framework.h @@ -29,11 +29,13 @@ enum mca_base_register_flag_t { typedef enum mca_base_register_flag_t mca_base_register_flag_t; enum mca_base_open_flag_t { - MCA_BASE_OPEN_DEFAULT = 0, + MCA_BASE_OPEN_DEFAULT = 0, /** Find components in mca_base_components_find. Used by mca_base_framework_open() when NOREGISTER is specified by the framework */ - MCA_BASE_OPEN_FIND_COMPONENTS = 1 + MCA_BASE_OPEN_FIND_COMPONENTS = 1, + /** Do not open DSO components */ + MCA_BASE_OPEN_STATIC_ONLY = 2, }; typedef enum mca_base_open_flag_t mca_base_open_flag_t; diff --git a/opal/mca/installdirs/base/installdirs_base_components.c b/opal/mca/installdirs/base/installdirs_base_components.c index 33a92db135d..0df268d3dc5 100644 --- a/opal/mca/installdirs/base/installdirs_base_components.c +++ b/opal/mca/installdirs/base/installdirs_base_components.c @@ -171,4 +171,4 @@ opal_installdirs_base_close(void) /* Declare the installdirs framework */ MCA_BASE_FRAMEWORK_DECLARE(opal, installdirs, NULL, NULL, opal_installdirs_base_open, opal_installdirs_base_close, mca_installdirs_base_static_components, - MCA_BASE_FRAMEWORK_FLAG_NOREGISTER); + MCA_BASE_FRAMEWORK_FLAG_NOREGISTER | MCA_BASE_FRAMEWORK_FLAG_NO_DSO); diff --git a/opal/runtime/opal_finalize.c b/opal/runtime/opal_finalize.c index b6d67bd075e..50d1932a7ff 100644 --- a/opal/runtime/opal_finalize.c +++ b/opal/runtime/opal_finalize.c @@ -10,7 +10,7 @@ * Copyright (c) 2004-2005 The Regents of the University of California. * All rights reserved. * Copyright (c) 2008-2015 Cisco Systems, Inc. All rights reserved. - * Copyright (c) 2010-2013 Los Alamos National Security, LLC. + * Copyright (c) 2010-2015 Los Alamos National Security, LLC. * All rights reserved. * Copyright (c) 2013-2015 Intel, Inc. All rights reserved * $COPYRIGHT$ @@ -160,9 +160,6 @@ opal_finalize(void) /* close the sec framework */ (void) mca_base_framework_close(&opal_sec_base_framework); - /* finalize the mca */ - mca_base_close(); - /* finalize util code */ opal_finalize_util(); diff --git a/opal/runtime/opal_init.c b/opal/runtime/opal_init.c index 89d6600590d..d351799b84e 100644 --- a/opal/runtime/opal_init.c +++ b/opal/runtime/opal_init.c @@ -13,7 +13,7 @@ * Copyright (c) 2007-2012 Cisco Systems, Inc. All rights reserved. * Copyright (c) 2007 Sun Microsystems, Inc. All rights reserved. * Copyright (c) 2009 Oak Ridge National Labs. All rights reserved. - * Copyright (c) 2010-2013 Los Alamos National Security, LLC. + * Copyright (c) 2010-2015 Los Alamos National Security, LLC. * All rights reserved. * Copyright (c) 2013-2014 Intel, Inc. All rights reserved * Copyright (c) 2015 Research Organization for Information Science @@ -354,6 +354,12 @@ opal_init_util(int* pargc, char*** pargv) goto return_error; } + /* initialize the mca */ + if (OPAL_SUCCESS != (ret = mca_base_open())) { + error = "mca_base_open"; + goto return_error; + } + return OPAL_SUCCESS; return_error: @@ -384,12 +390,6 @@ opal_init(int* pargc, char*** pargv) return ret; } - /* initialize the mca */ - if (OPAL_SUCCESS != (ret = mca_base_open())) { - error = "mca_base_open"; - goto return_error; - } - /* open hwloc - since this is a static framework, no * select is required */