/* Copyright (c) 2003-2018 Dovecot authors, see the included COPYING file */
#include "lib.h"
#include "array.h"
#include "str.h"
#include "sort.h"
#include "module-dir.h"
#ifdef HAVE_MODULES
#include <unistd.h>
#include <fcntl.h>
#include <dirent.h>
#include <dlfcn.h>
#ifndef RTLD_GLOBAL
# define RTLD_GLOBAL 0
#endif
#ifndef RTLD_NOW
# define RTLD_NOW 0
#endif
static const char *module_name_drop_suffix(const char *name);
{
/* clear out old errors */
(void)dlerror();
}
{
const char *error;
void *ret;
i_error("module %s: dlsym(%s) failed: %s",
}
}
return ret;
}
{
if (quiet)
}
{
/* dlclose()ing removes all symbols from valgrind's visibility.
if GDB environment is set, don't actually unload the module
(the GDB environment is used elsewhere too) */
}
}
static bool
{
return TRUE;
if (binary_dep == NULL)
return TRUE;
return TRUE;
else
return FALSE;
}
static bool
struct module *all_modules,
const char **error_r)
{
const char **deps;
struct module *m;
return TRUE;
break;
}
if (m == NULL) {
*deps);
" (you must set: %s=$%s %s)",
}
return FALSE;
}
}
return TRUE;
}
{
#ifndef __OpenBSD__
#else
void *handle;
int fd;
/* OpenBSD likes to print all "undefined symbol" errors to stderr.
if (fd == -1)
i_fatal("dup() failed: %m");
i_fatal("dup2() failed: %m");
i_fatal("dup2() failed: %m");
i_error("close() failed: %m");
return handle;
#endif
}
{
return TRUE;
str1++;
str2++;
}
return FALSE;
}
static int
const struct module_dir_load_settings *set,
struct module *all_modules,
{
void *handle;
const char *const *module_version;
if (set->ignore_dlopen_errors) {
i_debug("Skipping module %s, "
"because dlopen() failed: %s "
"(this is usually intentional, "
"so just ignore this message)",
}
return 0;
}
} else {
dlerror());
#ifdef RTLD_LAZY
/* try to give a better error message by lazily loading
the plugin and checking its dependencies */
return -1;
#else
return -1;
#endif
}
}
if (module_version != NULL &&
"Module is for different ABI version %s (we have %s)",
return -1;
}
/* get our init func */
"Module doesn't have %s function",
/* failed */
all_modules, error_r)) {
/* failed */
}
return -1;
}
return 1;
}
{
s1 += 3;
s2 += 3;
}
{
return FALSE;
}
return TRUE;
*names = "";
return TRUE;
}
}
return FALSE;
}
{
unsigned int i, count;
for (i = 0; i < count; i++) {
i_fatal("Multiple files for module %s: %s/%s, %s/%s",
}
}
{
return module;
}
}
return NULL;
}
{
}
{
unsigned int i, j;
if (module_names[0] == NULL)
return;
/* allow giving the module names also in non-base form.
convert them in here. */
for (i = 0; module_names[i] != NULL; i++)
/* @UNSAFE: drop duplicates */
module_names[j++] = module_names[i];
}
module_names[j] = NULL;
}
static bool
{
unsigned int i;
for (i = 0; module_names[i] != NULL; i++) {
return FALSE;
}
return TRUE;
}
static int
const char *dir, const char **module_names,
const struct module_dir_load_settings *set,
char **error_r)
{
struct dirent *d;
unsigned int i, count;
int ret;
if (module_names != NULL) {
return 0;
}
if (module_names != NULL) {
/* we were given a list of modules to load.
we can't fail. */
return -1;
}
}
if (name[0] == '.')
continue;
continue;
T_BEGIN {
} T_END;
}
if (errno != 0)
pool_unref(&pool);
return -1;
}
module_pos = &modules;
while (*module_pos != NULL)
else {
if (ret >= 0)
;
else if (module_names != NULL) {
i = count;
} else {
}
}
*module_pos = module;
}
} T_END;
pool_unref(&pool);
/* make sure all modules were found */
if (**module_names != '\0') {
*module_names, dir);
break;
}
}
}
}
const char *dir, const char *module_names,
const struct module_dir_load_settings *set,
const char **error_r)
{
int ret;
T_BEGIN {
if (module_names != NULL) {
}
} T_END;
return ret;
}
struct module *
const char *dir, const char *module_names,
const struct module_dir_load_settings *set)
{
const char *error;
if (module_names != NULL)
else
}
return new_modules;
}
{
if (!module->initialized) {
} T_END;
}
}
}
{
unsigned int i, count = 0;
count++;
}
if (count == 0)
return;
/* @UNSAFE: deinitialize in reverse order */
T_BEGIN {
i++;
}
}
for (i = 0; i < count; i++) {
}
} T_END;
}
{
/* Call all modules' deinit() first, so that they may still call each
others' functions. */
}
}
#else
#ifndef MODULE_SUFFIX
#endif
struct module *
const char *dir ATTR_UNUSED,
const char *module_names,
{
if (module_names == NULL)
else {
}
return NULL;
}
{
}
{
}
{
}
const char *name ATTR_UNUSED)
{
return NULL;
}
const char *symbol ATTR_UNUSED)
{
return NULL;
}
const char *symbol ATTR_UNUSED)
{
return NULL;
}
#endif
const struct module_dir_load_settings *set)
{
}
{
const char *p;
/* [lib][nn_]name(.so) */
fname += 3;
for (p = fname; *p != '\0'; p++) {
if (*p < '0' || *p > '9')
break;
}
if (*p == '_')
fname = p + 1;
if (p == NULL)
return fname;
return t_strdup_until(fname, p);
}
{
return name;
}
{
}