#include "src/include/pmix_config.h"
#ifdef HAVE_SYS_TYPES_H
# include <sys/types.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif
#include "pmix_common.h"
#include "src/class/pmix_hash_table.h"
#include "src/class/pmix_list.h"
#include "src/include/pmix_globals.h"
#include "src/mca/base/pmix_base.h"
#include "src/mca/base/pmix_mca_base_component_repository.h"
#include "src/mca/mca.h"
#include "src/mca/pdl/base/base.h"
#include "src/util/pmix_printf.h"
#include "src/util/pmix_basename.h"
#include "src/util/pmix_show_help.h"
#if PMIX_HAVE_PDL_SUPPORT
static void ri_constructor(pmix_mca_base_component_repository_item_t *ri);
static void ri_destructor(pmix_mca_base_component_repository_item_t *ri);
PMIX_CLASS_INSTANCE(pmix_mca_base_component_repository_item_t, pmix_list_item_t, ri_constructor,
ri_destructor);
#endif
static void clf_constructor(pmix_object_t *obj)
{
pmix_mca_base_failed_component_t *cli = (pmix_mca_base_failed_component_t *) obj;
cli->comp = NULL;
cli->error_msg = NULL;
}
static void clf_destructor(pmix_object_t *obj)
{
pmix_mca_base_failed_component_t *cli = (pmix_mca_base_failed_component_t *) obj;
cli->comp = NULL;
if (NULL != cli->error_msg) {
free(cli->error_msg);
cli->error_msg = NULL;
}
}
PMIX_CLASS_INSTANCE(pmix_mca_base_failed_component_t, pmix_list_item_t, clf_constructor,
clf_destructor);
static bool initialized = false;
#if PMIX_HAVE_PDL_SUPPORT
static pmix_hash_table_t pmix_mca_base_component_repository;
# define STRINGIFYX(x) # x
# define STRINGIFY(x) STRINGIFYX(x)
static int process_repository_item(const char *filename, void *data)
{
char *project = (char*)data;
char *name;
char *type;
pmix_mca_base_component_repository_item_t *ri;
pmix_list_t *component_list;
char *base, *prefix;
int ret;
size_t prefixlen;
base = pmix_basename(filename);
if (NULL == base) {
return PMIX_ERROR;
}
pmix_asprintf(&prefix, "%s_mca_", project);
pmix_asprintf(&type, "lib%s_mca_", project);
if (0 != strncmp(base, prefix, strlen(prefix)) &&
0 != strncmp(base, type, strlen(type))) {
if (pmix_mca_base_show_load_errors(NULL, NULL)) {
pmix_output_verbose(
PMIX_MCA_BASE_VERBOSE_INFO, 0,
"mca:base:process_repository_item filename %s has unexpected prefix - expected:\n\t%s\nor\n\t%s",
filename, prefix, type);
}
free(base);
free(prefix);
free(type);
return PMIX_SUCCESS;
}
free(type);
prefixlen = strlen(prefix);
type = &base[prefixlen];
name = strchr(type, '_');
if (NULL == name) {
free(base);
free(prefix);
return PMIX_ERR_BAD_PARAM;
}
*name = '\0';
++name;
free(prefix);
ret = pmix_hash_table_get_value_ptr(&pmix_mca_base_component_repository, type, strlen(type),
(void **) &component_list);
if (PMIX_SUCCESS != ret) {
component_list = PMIX_NEW(pmix_list_t);
if (NULL == component_list) {
free(base);
return PMIX_ERR_OUT_OF_RESOURCE;
}
ret = pmix_hash_table_set_value_ptr(&pmix_mca_base_component_repository, type, strlen(type),
(void *) component_list);
if (PMIX_SUCCESS != ret) {
free(base);
PMIX_RELEASE(component_list);
return ret;
}
}
PMIX_LIST_FOREACH (ri, component_list, pmix_mca_base_component_repository_item_t) {
if (0 == strcmp(ri->ri_name, name)) {
free(base);
return PMIX_SUCCESS;
}
}
ri = PMIX_NEW(pmix_mca_base_component_repository_item_t);
if (NULL == ri) {
free(base);
free(project);
return PMIX_ERR_OUT_OF_RESOURCE;
}
ri->ri_base = base;
ri->ri_project = strdup(project);
ri->ri_path = strdup(filename);
if (NULL == ri->ri_path) {
PMIX_RELEASE(ri);
return PMIX_ERR_OUT_OF_RESOURCE;
}
ri->ri_type[PMIX_MCA_BASE_MAX_TYPE_NAME_LEN] = '\0';
pmix_strncpy(ri->ri_type, type, PMIX_MCA_BASE_MAX_TYPE_NAME_LEN);
ri->ri_name[PMIX_MCA_BASE_MAX_TYPE_NAME_LEN] = '\0';
pmix_strncpy(ri->ri_name, name, PMIX_MCA_BASE_MAX_COMPONENT_NAME_LEN);
pmix_list_append(component_list, &ri->super);
return PMIX_SUCCESS;
}
static int file_exists(const char *filename, const char *ext)
{
char *final;
int ret;
if (NULL == ext) {
return access(filename, F_OK) == 0;
}
ret = asprintf(&final, "%s.%s", filename, ext);
if (0 > ret || NULL == final) {
return 0;
}
ret = access(final, F_OK);
free(final);
return (0 == ret);
}
#endif
int pmix_mca_base_component_repository_add(const char *project,
const char *path)
{
#if PMIX_HAVE_PDL_SUPPORT
char *path_to_use = NULL, *dir, *ctx;
const char sep[] = {PMIX_ENV_SEP, '\0'};
if (NULL == path) {
return PMIX_SUCCESS;
}
path_to_use = strdup(path);
dir = strtok_r(path_to_use, sep, &ctx);
do {
if (0 != pmix_pdl_foreachfile(dir, process_repository_item, (void*)project) &&
!(0 == strcmp(dir, pmix_mca_base_system_default_path) ||
0 == strcmp(dir, pmix_mca_base_user_default_path))) {
pmix_show_help("help-pmix-mca-base.txt", "failed to add component dir", true, dir);
}
} while (NULL != (dir = strtok_r(NULL, sep, &ctx)));
free(path_to_use);
#else
PMIX_HIDE_UNUSED_PARAMS(project, path);
#endif
return PMIX_SUCCESS;
}
int pmix_mca_base_component_repository_init(void)
{
#if PMIX_HAVE_PDL_SUPPORT
char **projects = NULL, *pathstr;
char project[PMIX_MCA_BASE_MAX_TYPE_NAME_LEN + 1];
int m, n;
int ret;
if (!initialized) {
ret = pmix_mca_base_framework_open(&pmix_pdl_base_framework,
PMIX_MCA_BASE_OPEN_DEFAULT);
if (PMIX_SUCCESS != ret) {
pmix_output(0,
"%s %d:%s failed -- process will likely abort (open the dl framework "
"returned %d instead of PMIX_SUCCESS)\n",
__FILE__, __LINE__, __func__, ret);
return ret;
}
pmix_pdl_base_select();
PMIX_CONSTRUCT(&pmix_mca_base_component_repository, pmix_hash_table_t);
ret = pmix_hash_table_init(&pmix_mca_base_component_repository, 128);
if (PMIX_SUCCESS != ret) {
(void) pmix_mca_base_framework_close(&pmix_pdl_base_framework);
return ret;
}
initialized = true;
}
projects = PMIx_Argv_split(pmix_mca_base_component_path, ';');
for (n=0; NULL != projects[n]; n++) {
for (m=0; '@' != projects[n][m]; m++) {
project[m] = projects[n][m];
}
project[m] = '\0';
++m;
pathstr = &projects[n][m];
ret = pmix_mca_base_component_repository_add(project, pathstr);
if (PMIX_SUCCESS != ret) {
PMIX_DESTRUCT(&pmix_mca_base_component_repository);
(void) pmix_mca_base_framework_close(&pmix_pdl_base_framework);
PMIx_Argv_free(projects);
return ret;
}
}
PMIx_Argv_free(projects);
#endif
return PMIX_SUCCESS;
}
int pmix_mca_base_component_repository_get_components(pmix_mca_base_framework_t *framework,
pmix_list_t **framework_components)
{
*framework_components = NULL;
#if PMIX_HAVE_PDL_SUPPORT
return pmix_hash_table_get_value_ptr(&pmix_mca_base_component_repository,
framework->framework_name,
strlen(framework->framework_name),
(void **) framework_components);
#else
PMIX_HIDE_UNUSED_PARAMS(framework);
return PMIX_ERR_NOT_FOUND;
#endif
}
#if PMIX_HAVE_PDL_SUPPORT
static void
pmix_mca_base_component_repository_release_internal(pmix_mca_base_component_repository_item_t *ri)
{
int group_id;
group_id = pmix_mca_base_var_group_find(NULL, ri->ri_type, ri->ri_name);
if (0 <= group_id) {
pmix_mca_base_var_group_deregister(group_id);
}
if (ri->ri_dlhandle) {
pmix_pdl_close(ri->ri_dlhandle);
ri->ri_dlhandle = NULL;
}
}
#endif
#if PMIX_HAVE_PDL_SUPPORT
static pmix_mca_base_component_repository_item_t *find_component(const char *type, const char *name)
{
pmix_mca_base_component_repository_item_t *ri;
pmix_list_t *component_list;
int ret;
ret = pmix_hash_table_get_value_ptr(&pmix_mca_base_component_repository, type, strlen(type),
(void **) &component_list);
if (PMIX_SUCCESS != ret) {
return NULL;
}
PMIX_LIST_FOREACH (ri, component_list, pmix_mca_base_component_repository_item_t) {
if (0 == strcmp(ri->ri_name, name)) {
return ri;
}
}
return NULL;
}
#endif
void pmix_mca_base_component_repository_release(const pmix_mca_base_component_t *component)
{
#if PMIX_HAVE_PDL_SUPPORT
pmix_mca_base_component_repository_item_t *ri;
ri = find_component(component->pmix_mca_type_name, component->pmix_mca_component_name);
if (NULL != ri && !(--ri->ri_refcnt)) {
pmix_mca_base_component_repository_release_internal(ri);
}
#else
PMIX_HIDE_UNUSED_PARAMS(component);
#endif
}
int pmix_mca_base_component_repository_retain_component(const char *type, const char *name)
{
#if PMIX_HAVE_PDL_SUPPORT
pmix_mca_base_component_repository_item_t *ri = find_component(type, name);
if (NULL != ri) {
++ri->ri_refcnt;
return PMIX_SUCCESS;
}
return PMIX_ERR_NOT_FOUND;
#else
PMIX_HIDE_UNUSED_PARAMS(type, name);
return PMIX_ERR_NOT_SUPPORTED;
#endif
}
int pmix_mca_base_component_repository_open(pmix_mca_base_framework_t *framework,
pmix_mca_base_component_repository_item_t *ri)
{
#if PMIX_HAVE_PDL_SUPPORT
pmix_mca_base_component_t *component_struct;
pmix_mca_base_component_list_item_t *mitem = NULL;
char *struct_name = NULL;
int vl, ret;
pmix_output_verbose(PMIX_MCA_BASE_VERBOSE_INFO, 0,
"pmix_mca_base_component_repository_open: examining dynamic "
"%s MCA component \"%s\" at path %s",
ri->ri_type, ri->ri_name, ri->ri_path);
vl = pmix_mca_base_show_load_errors(ri->ri_type,
ri->ri_name) ? PMIX_MCA_BASE_VERBOSE_ERROR : PMIX_MCA_BASE_VERBOSE_INFO;;
PMIX_LIST_FOREACH (mitem, &framework->framework_components,
pmix_mca_base_component_list_item_t) {
if (0 == strcmp(mitem->cli_component->pmix_mca_component_name, ri->ri_name)) {
pmix_output_verbose(
PMIX_MCA_BASE_VERBOSE_INFO, 0,
"pmix_mca_base_component_repository_open: already loaded (ignored)");
return PMIX_ERR_BAD_PARAM;
}
}
mitem = NULL;
if (NULL != ri->ri_dlhandle) {
pmix_output_verbose(
PMIX_MCA_BASE_VERBOSE_INFO, 0,
"pmix_mca_base_component_repository_open: already loaded. returning cached component");
mitem = PMIX_NEW(pmix_mca_base_component_list_item_t);
if (NULL == mitem) {
return PMIX_ERR_OUT_OF_RESOURCE;
}
mitem->cli_component = ri->ri_component_struct;
pmix_list_append(&framework->framework_components, &mitem->super);
return PMIX_SUCCESS;
}
if (0 != strcmp(ri->ri_type, framework->framework_name)) {
assert(0);
return PMIX_ERR_NOT_SUPPORTED;
}
char *err_msg = NULL;
if (PMIX_SUCCESS != pmix_pdl_open(ri->ri_path, true, false, &ri->ri_dlhandle, &err_msg)) {
if (NULL == err_msg) {
err_msg = strdup("pmix_dl_open() error message was NULL!");
} else if (file_exists(ri->ri_path, "lo") || file_exists(ri->ri_path, "so")
|| file_exists(ri->ri_path, "dylib") || file_exists(ri->ri_path, "dll")) {
char *tmp;
pmix_asprintf(&tmp,
"\n dlopen error: %s\n Perhaps a missing symbol, or compiled for a different version of %s?",
err_msg, framework->framework_project);
err_msg = tmp;
}
pmix_output_verbose(
vl, 0, "pmix_mca_base_component_repository_open: unable to open %s: %s (ignored)",
ri->ri_path, err_msg);
if (pmix_mca_base_component_track_load_errors) {
pmix_mca_base_failed_component_t *f_comp = PMIX_NEW(pmix_mca_base_failed_component_t);
f_comp->comp = ri;
if (0 > asprintf(&(f_comp->error_msg), "%s", err_msg)) {
PMIX_RELEASE(f_comp);
free(err_msg);
return PMIX_ERR_BAD_PARAM;
}
pmix_list_append(&framework->framework_failed_components, &f_comp->super);
}
free(err_msg);
return PMIX_ERR_BAD_PARAM;
}
do {
pmix_asprintf(&struct_name, "%s_mca_%s_%s_component",
ri->ri_project, ri->ri_type, ri->ri_name);
mitem = PMIX_NEW(pmix_mca_base_component_list_item_t);
if (NULL == mitem) {
ret = PMIX_ERR_OUT_OF_RESOURCE;
break;
}
err_msg = NULL;
ret = pmix_pdl_lookup(ri->ri_dlhandle, struct_name, (void **) &component_struct, &err_msg);
if (PMIX_SUCCESS != ret || NULL == component_struct) {
if (NULL == err_msg) {
err_msg = "pmix_dl_lookup() error message was NULL!";
}
pmix_output_verbose(
vl, 0,
"pmix_mca_base_component_repository_open: \"%s\" does not appear to be a valid "
"%s MCA dynamic component (ignored):\n %s (ret %d)",
ri->ri_base, ri->ri_type, err_msg, ret);
ret = PMIX_ERR_BAD_PARAM;
break;
}
free(struct_name);
struct_name = NULL;
if (!(PMIX_MCA_BASE_VERSION_MAJOR == component_struct->pmix_mca_major_version
&& PMIX_MCA_BASE_VERSION_MINOR == component_struct->pmix_mca_minor_version)) {
pmix_output_verbose(
vl, 0,
"pmix_mca_base_component_repository_open: %s\n \"%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->pmix_mca_major_version,
component_struct->pmix_mca_minor_version,
component_struct->pmix_mca_release_version, PMIX_MCA_BASE_VERSION_MAJOR,
PMIX_MCA_BASE_VERSION_MINOR, PMIX_MCA_BASE_VERSION_RELEASE);
ret = PMIX_ERR_BAD_PARAM;
break;
}
if (0 != strcmp(component_struct->pmix_mca_type_name, ri->ri_type)
|| 0 != strcmp(component_struct->pmix_mca_component_name, ri->ri_name)) {
pmix_output_verbose(
vl, 0,
"Component file data does not match filename:\n %s (%s / %s) != %s %s -- ignored",
ri->ri_path, ri->ri_type, ri->ri_name, component_struct->pmix_mca_type_name,
component_struct->pmix_mca_component_name);
ret = PMIX_ERR_BAD_PARAM;
break;
}
ri->ri_component_struct = mitem->cli_component = component_struct;
ri->ri_refcnt = 1;
pmix_list_append(&framework->framework_components, &mitem->super);
pmix_output_verbose(PMIX_MCA_BASE_VERBOSE_INFO, 0,
"pmix_mca_base_component_repository_open: opened dynamic %s MCA "
"component \"%s\"",
ri->ri_type, ri->ri_name);
return PMIX_SUCCESS;
} while (0);
if (mitem) {
PMIX_RELEASE(mitem);
}
if (struct_name) {
free(struct_name);
}
pmix_pdl_close(ri->ri_dlhandle);
ri->ri_dlhandle = NULL;
return ret;
#else
PMIX_HIDE_UNUSED_PARAMS(framework, ri);
return PMIX_ERR_NOT_SUPPORTED;
#endif
}
void pmix_mca_base_component_repository_finalize(void)
{
if (!initialized) {
return;
}
initialized = false;
#if PMIX_HAVE_PDL_SUPPORT
pmix_list_t *component_list;
void *node, *key;
size_t key_size;
int ret;
ret = pmix_hash_table_get_first_key_ptr(&pmix_mca_base_component_repository, &key, &key_size,
(void **) &component_list, &node);
while (PMIX_SUCCESS == ret) {
PMIX_LIST_RELEASE(component_list);
ret = pmix_hash_table_get_next_key_ptr(&pmix_mca_base_component_repository, &key, &key_size,
(void **) &component_list, node, &node);
}
(void) pmix_mca_base_framework_close(&pmix_pdl_base_framework);
PMIX_DESTRUCT(&pmix_mca_base_component_repository);
#endif
}
#if PMIX_HAVE_PDL_SUPPORT
static void ri_constructor(pmix_mca_base_component_repository_item_t *ri)
{
memset(ri->ri_type, 0, sizeof(ri->ri_type));
ri->ri_dlhandle = NULL;
ri->ri_component_struct = NULL;
ri->ri_path = NULL;
}
static void ri_destructor(pmix_mca_base_component_repository_item_t *ri)
{
pmix_mca_base_component_repository_release_internal(ri);
if (NULL != ri->ri_project) {
free(ri->ri_project);
}
if (NULL != ri->ri_path) {
free(ri->ri_path);
}
if (NULL != ri->ri_base) {
free(ri->ri_base);
}
}
#endif