#include "src/include/pmix_config.h"
#include <dirent.h>
#include <dlfcn.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include "pmix_common.h"
#include "src/mca/pdl/pdl.h"
#include "src/util/pmix_argv.h"
#include "src/util/pmix_error.h"
#include "pdl_pdlopen.h"
static void do_pdlopen(const char *fname, int flags, void **handle, char **err_msg)
{
assert(handle);
*handle = dlopen(fname, flags);
if (NULL != err_msg) {
if (NULL != *handle) {
*err_msg = NULL;
} else {
*err_msg = dlerror();
}
}
}
static int pdlopen_open(const char *fname, bool use_ext, bool private_namespace,
pmix_pdl_handle_t **handle, char **err_msg)
{
int rc;
assert(handle);
*handle = NULL;
int flags = RTLD_LAZY;
if (private_namespace) {
flags |= RTLD_LOCAL;
} else {
flags |= RTLD_GLOBAL;
}
void *local_handle = NULL;
if (use_ext && NULL != fname) {
int i;
char *ext;
for (i = 0, ext = pmix_mca_pdl_pdlopen_component.filename_suffixes[i]; NULL != ext;
ext = pmix_mca_pdl_pdlopen_component.filename_suffixes[++i]) {
char *name;
rc = asprintf(&name, "%s%s", fname, ext);
if (0 > rc) {
return PMIX_ERR_NOMEM;
}
if (NULL == name) {
return PMIX_ERR_IN_ERRNO;
}
struct stat buf;
if (stat(name, &buf) < 0) {
if (NULL != err_msg) {
rc = asprintf(err_msg, "File %s not found", name);
if (0 > rc) {
free(name);
return PMIX_ERR_NOMEM;
}
}
free(name);
continue;
}
do_pdlopen(name, flags, &local_handle, err_msg);
free(name);
break;
}
}
else {
do_pdlopen(fname, flags, &local_handle, err_msg);
}
if (NULL != local_handle) {
*handle = calloc(1, sizeof(pmix_pdl_handle_t));
(*handle)->dlopen_handle = local_handle;
#if PMIX_ENABLE_DEBUG
if (NULL != fname) {
(*handle)->filename = strdup(fname);
} else {
(*handle)->filename = strdup("(null)");
}
#endif
}
return (NULL != local_handle) ? PMIX_SUCCESS : PMIX_ERROR;
}
static int pdlopen_lookup(pmix_pdl_handle_t *handle, const char *symbol, void **ptr, char **err_msg)
{
assert(handle);
assert(handle->dlopen_handle);
assert(symbol);
assert(ptr);
*ptr = dlsym(handle->dlopen_handle, symbol);
if (NULL != *ptr) {
return PMIX_SUCCESS;
}
if (NULL != err_msg) {
*err_msg = dlerror();
}
return PMIX_ERROR;
}
static int pdlopen_close(pmix_pdl_handle_t *handle)
{
assert(handle);
int ret;
ret = dlclose(handle->dlopen_handle);
#if PMIX_ENABLE_DEBUG
free(handle->filename);
#endif
free(handle);
return ret;
}
static int pdlopen_foreachfile(const char *search_path,
int (*func)(const char *filename, void *data), void *data)
{
int ret;
DIR *dp = NULL;
char **dirs = NULL;
char **good_files = NULL;
dirs = PMIx_Argv_split(search_path, PMIX_ENV_SEP);
for (int i = 0; NULL != dirs && NULL != dirs[i]; ++i) {
dp = opendir(dirs[i]);
if (NULL == dp) {
ret = PMIX_ERR_IN_ERRNO;
goto error;
}
struct dirent *de;
while (NULL != (de = readdir(dp))) {
char *abs_name = NULL;
ret = asprintf(&abs_name, "%s/%s", dirs[i], de->d_name);
if (0 > ret) {
goto error;
}
if (NULL == abs_name) {
ret = PMIX_ERR_IN_ERRNO;
goto error;
}
struct stat buf;
if (stat(abs_name, &buf) < 0) {
free(abs_name);
ret = PMIX_ERR_IN_ERRNO;
goto error;
}
if (!S_ISREG(buf.st_mode)) {
free(abs_name);
continue;
}
char *ptr = strrchr(abs_name, '.');
if (NULL != ptr) {
if (strcmp(ptr, ".la") == 0 || strcmp(ptr, ".lo") == 0) {
free(abs_name);
continue;
}
if (strcmp(ptr, ".o") == 0) {
free(abs_name);
continue;
}
*ptr = '\0';
}
bool found = false;
for (int j = 0; NULL != good_files && NULL != good_files[j]; ++j) {
if (strcmp(good_files[j], abs_name) == 0) {
found = true;
break;
}
}
if (!found) {
PMIx_Argv_append_nosize(&good_files, abs_name);
}
free(abs_name);
}
closedir(dp);
}
dp = NULL;
if (NULL != good_files) {
for (int i = 0; NULL != good_files[i]; ++i) {
ret = func(good_files[i], data);
if (PMIX_SUCCESS != ret) {
goto error;
}
}
}
ret = PMIX_SUCCESS;
error:
if (NULL != dp) {
closedir(dp);
}
if (NULL != dirs) {
PMIx_Argv_free(dirs);
}
if (NULL != good_files) {
PMIx_Argv_free(good_files);
}
return ret;
}
pmix_pdl_base_module_t pmix_pdl_pdlopen_module = {.open = pdlopen_open,
.lookup = pdlopen_lookup,
.close = pdlopen_close,
.foreachfile = pdlopen_foreachfile};