7d340c0c26
Works on systems with dlopen (e.g., Linux and OS X). It requires dlfcn.h and libdl, which many systems have installed by default.
264 строки
6.1 KiB
C
264 строки
6.1 KiB
C
/*
|
|
* Copyright (c) 2015 Cisco Systems, Inc. All rights reserved.
|
|
* $COPYRIGHT$
|
|
*
|
|
* Additional copyrights may follow
|
|
*
|
|
* $HEADER$
|
|
*/
|
|
|
|
#include "opal_config.h"
|
|
|
|
#include <stdlib.h>
|
|
#include <dlfcn.h>
|
|
#include <sys/types.h>
|
|
#include <dirent.h>
|
|
#include <sys/stat.h>
|
|
#include <unistd.h>
|
|
|
|
#include "opal/constants.h"
|
|
#include "opal/mca/dl/dl.h"
|
|
#include "opal/util/argv.h"
|
|
|
|
#include "dl_dlopen.h"
|
|
|
|
|
|
/*
|
|
* Trivial helper function to avoid replicating code
|
|
*/
|
|
static void do_dlopen(const char *fname, int flags,
|
|
void **handle, char **err_msg)
|
|
{
|
|
assert(fname);
|
|
assert(handle);
|
|
|
|
*handle = dlopen(fname, flags);
|
|
|
|
if (NULL != err_msg) {
|
|
if (NULL != *handle) {
|
|
*err_msg = NULL;
|
|
} else {
|
|
*err_msg = dlerror();
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
static int dlopen_open(const char *fname, bool use_ext, bool private_namespace,
|
|
opal_dl_handle_t **handle, char **err_msg)
|
|
{
|
|
assert(fname);
|
|
assert(handle);
|
|
|
|
*handle = NULL;
|
|
|
|
/* Setup the dlopen flags */
|
|
int flags = RTLD_LAZY;
|
|
if (private_namespace) {
|
|
flags |= RTLD_LOCAL;
|
|
} else {
|
|
flags |= RTLD_GLOBAL;
|
|
}
|
|
|
|
/* If the caller wants to use filename extensions, loop through
|
|
them */
|
|
void *local_handle = NULL;
|
|
if (use_ext) {
|
|
int i;
|
|
char *ext;
|
|
|
|
for (i = 0, ext = mca_dl_dlopen_component.filename_suffixes[i];
|
|
NULL != ext;
|
|
ext = mca_dl_dlopen_component.filename_suffixes[++i]) {
|
|
char *name;
|
|
|
|
asprintf(&name, "%s%s", fname, ext);
|
|
if (NULL == name) {
|
|
return OPAL_ERR_IN_ERRNO;
|
|
}
|
|
|
|
/* Does the file exist? */
|
|
struct stat buf;
|
|
if (stat(name, &buf) < 0) {
|
|
free(name);
|
|
if (NULL != err_msg) {
|
|
*err_msg = "File not found";
|
|
}
|
|
continue;
|
|
}
|
|
|
|
/* Yes, the file exists -- try to dlopen it. If we can't
|
|
dlopen it, bail. */
|
|
do_dlopen(name, flags, &local_handle, err_msg);
|
|
free(name);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Otherwise, the caller does not want to use filename extensions,
|
|
so just use the single filename that the caller provided */
|
|
else {
|
|
do_dlopen(fname, flags, &local_handle, err_msg);
|
|
}
|
|
|
|
if (NULL != local_handle) {
|
|
*handle = calloc(1, sizeof(opal_dl_handle_t));
|
|
(*handle)->dlopen_handle = local_handle;
|
|
|
|
#if OPAL_ENABLE_DEBUG
|
|
(*handle)->filename = strdup(fname);
|
|
#endif
|
|
}
|
|
return (NULL != local_handle) ? OPAL_SUCCESS : OPAL_ERROR;
|
|
}
|
|
|
|
|
|
static int dlopen_lookup(opal_dl_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 OPAL_SUCCESS;
|
|
}
|
|
|
|
if (NULL != err_msg) {
|
|
*err_msg = dlerror();
|
|
}
|
|
return OPAL_ERROR;
|
|
}
|
|
|
|
|
|
static int dlopen_close(opal_dl_handle_t *handle)
|
|
{
|
|
assert(handle);
|
|
|
|
int ret;
|
|
ret = dlclose(handle->dlopen_handle);
|
|
|
|
#if OPAL_ENABLE_DEBUG
|
|
free(handle->filename);
|
|
#endif
|
|
free(handle);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* Scan all the files in a directory (or path) and invoke a callback
|
|
* on each one.
|
|
*/
|
|
static int dlopen_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 = opal_argv_split(search_path, OPAL_ENV_SEP);
|
|
for (int i = 0; NULL != dirs[i]; ++i) {
|
|
|
|
dp = opendir(dirs[i]);
|
|
if (NULL == dp) {
|
|
return OPAL_ERR_IN_ERRNO;
|
|
}
|
|
|
|
struct dirent *de;
|
|
while (NULL != (de = readdir(dp))) {
|
|
|
|
/* Make the absolute path name */
|
|
char *abs_name = NULL;
|
|
asprintf(&abs_name, "%s/%s", dirs[i], de->d_name);
|
|
if (NULL == abs_name) {
|
|
ret = OPAL_ERR_IN_ERRNO;
|
|
goto error;
|
|
}
|
|
|
|
/* Stat the file */
|
|
struct stat buf;
|
|
if (stat(abs_name, &buf) < 0) {
|
|
free(abs_name);
|
|
ret = OPAL_ERR_IN_ERRNO;
|
|
goto error;
|
|
}
|
|
|
|
/* Skip if not a file */
|
|
if (!S_ISREG(buf.st_mode)) {
|
|
free(abs_name);
|
|
continue;
|
|
}
|
|
|
|
/* Find the suffix */
|
|
char *ptr = strrchr(abs_name, '.');
|
|
if (NULL != ptr) {
|
|
|
|
/* Skip libtool files */
|
|
if (strcmp(ptr, ".la") == 0 ||
|
|
strcmp(ptr, ".lo") == 0) {
|
|
continue;
|
|
}
|
|
|
|
*ptr = '\0';
|
|
}
|
|
|
|
/* Have we already found this file? Or already found a
|
|
file with the same basename (but different suffix)? */
|
|
if (NULL != good_files) {
|
|
for (int j = 0; NULL != good_files[j]; ++j) {
|
|
if (strcmp(good_files[j], abs_name) == 0) {
|
|
free(abs_name);
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
opal_argv_append_nosize(&good_files, abs_name);
|
|
free(abs_name);
|
|
}
|
|
}
|
|
closedir(dp);
|
|
dp = NULL;
|
|
|
|
/* Invoke the callback on all the found files */
|
|
if (NULL != good_files) {
|
|
for (int i = 0; NULL != good_files[i]; ++i) {
|
|
ret = func(good_files[i], data);
|
|
if (OPAL_SUCCESS != ret) {
|
|
goto error;
|
|
}
|
|
}
|
|
}
|
|
|
|
ret = OPAL_SUCCESS;
|
|
|
|
error:
|
|
if (NULL != dp) {
|
|
closedir(dp);
|
|
}
|
|
if (NULL != dirs) {
|
|
opal_argv_free(dirs);
|
|
}
|
|
if (NULL != good_files) {
|
|
opal_argv_free(good_files);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
/*
|
|
* Module definition
|
|
*/
|
|
opal_dl_base_module_t opal_dl_dlopen_module = {
|
|
.open = dlopen_open,
|
|
.lookup = dlopen_lookup,
|
|
.close = dlopen_close,
|
|
.foreachfile = dlopen_foreachfile
|
|
};
|