|
Packit |
c32a2d |
/*
|
|
Packit |
c32a2d |
module.c: modular code loader
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
copyright 1995-2015 by the mpg123 project - free software under the terms of the LGPL 2.1
|
|
Packit |
c32a2d |
see COPYING and AUTHORS files in distribution or http://mpg123.org
|
|
Packit |
c32a2d |
initially written by Nicholas J Humfrey
|
|
Packit |
c32a2d |
*/
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
/* Need snprintf(). */
|
|
Packit |
c32a2d |
#define _DEFAULT_SOURCE
|
|
Packit |
c32a2d |
#define _BSD_SOURCE
|
|
Packit |
c32a2d |
#include "config.h"
|
|
Packit |
c32a2d |
#include "intsym.h"
|
|
Packit |
c32a2d |
#include "stringlists.h"
|
|
Packit |
c32a2d |
#include "compat.h"
|
|
Packit |
c32a2d |
#include <errno.h>
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
#include "module.h"
|
|
Packit |
c32a2d |
#include "debug.h"
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
#ifndef USE_MODULES
|
|
Packit |
c32a2d |
#error This is a build without modules. Why am I here?
|
|
Packit |
c32a2d |
#endif
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
#define MODULE_SYMBOL_PREFIX "mpg123_"
|
|
Packit |
c32a2d |
#define MODULE_SYMBOL_SUFFIX "_module_info"
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
/* Windows code can convert these from UTF-8 (or ASCII, does not matter)
|
|
Packit |
c32a2d |
to wide and then replace / by \. No need to define another list. */
|
|
Packit |
c32a2d |
static const char* modulesearch[] =
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
"../lib/mpg123"
|
|
Packit |
c32a2d |
,"plugins"
|
|
Packit |
c32a2d |
,"libout123/modules/.libs"
|
|
Packit |
c32a2d |
,"libout123/modules"
|
|
Packit |
c32a2d |
,"../libout123/modules/.libs"
|
|
Packit |
c32a2d |
,"../libout123/modules"
|
|
Packit |
c32a2d |
};
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
static char *get_module_dir(int verbose, const char* bindir)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
char *moddir = NULL;
|
|
Packit |
c32a2d |
char *defaultdir;
|
|
Packit |
c32a2d |
/* First the environment override, then relative to bindir, then installation prefix. */
|
|
Packit |
c32a2d |
defaultdir = compat_getenv("MPG123_MODDIR");
|
|
Packit |
c32a2d |
if(defaultdir)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
if(verbose > 1)
|
|
Packit |
c32a2d |
fprintf(stderr, "Trying module directory from environment: %s\n", defaultdir);
|
|
Packit |
c32a2d |
if(compat_isdir(defaultdir))
|
|
Packit |
c32a2d |
moddir = defaultdir;
|
|
Packit |
c32a2d |
else
|
|
Packit |
c32a2d |
free(defaultdir);
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
else
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
if(bindir) /* Search relative to binary. */
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
size_t i;
|
|
Packit |
c32a2d |
if(verbose > 1)
|
|
Packit |
c32a2d |
fprintf(stderr, "Module dir search relative to: %s\n", bindir);
|
|
Packit |
c32a2d |
for(i=0; i
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
moddir = compat_catpath(bindir, modulesearch[i]);
|
|
Packit |
c32a2d |
if(!moddir)
|
|
Packit |
c32a2d |
continue;
|
|
Packit |
c32a2d |
if(verbose > 1)
|
|
Packit |
c32a2d |
fprintf(stderr, "Looking for module dir: %s\n", moddir);
|
|
Packit |
c32a2d |
if(compat_isdir(moddir))
|
|
Packit |
c32a2d |
break; /* found it! */
|
|
Packit |
c32a2d |
else
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
free(moddir);
|
|
Packit |
c32a2d |
moddir=NULL;
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
if(!moddir) /* Resort to installation prefix. */
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
if(compat_isdir(PKGLIBDIR))
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
if(verbose > 1)
|
|
Packit |
c32a2d |
fprintf(stderr, "Using default module dir: %s\n", PKGLIBDIR);
|
|
Packit |
c32a2d |
moddir = compat_strdup(PKGLIBDIR);
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
if(verbose > 1)
|
|
Packit |
c32a2d |
fprintf(stderr, "Module dir: %s\n", moddir != NULL ? moddir : "<nil>");
|
|
Packit |
c32a2d |
return moddir;
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
/* Open a module in given directory. */
|
|
Packit |
c32a2d |
mpg123_module_t* open_module_here( const char *dir, const char* type
|
|
Packit |
c32a2d |
, const char* name, int verbose )
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
void *handle = NULL;
|
|
Packit |
c32a2d |
mpg123_module_t *module = NULL;
|
|
Packit |
c32a2d |
char *module_file = NULL;
|
|
Packit |
c32a2d |
size_t module_file_len = 0;
|
|
Packit |
c32a2d |
char *module_symbol = NULL;
|
|
Packit |
c32a2d |
size_t module_symbol_len = 0;
|
|
Packit |
c32a2d |
char *module_path = NULL;
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
/* Work out the path of the module to open */
|
|
Packit |
c32a2d |
module_file_len = strlen(type) + 1 + strlen(name) + strlen(LT_MODULE_EXT) + 1;
|
|
Packit |
c32a2d |
module_file = malloc(module_file_len);
|
|
Packit |
c32a2d |
if(!module_file)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
if(verbose > -1)
|
|
Packit |
c32a2d |
error1( "Failed to allocate memory for module name: %s", strerror(errno) );
|
|
Packit |
c32a2d |
return NULL;
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
snprintf(module_file, module_file_len, "%s_%s%s", type, name, LT_MODULE_EXT);
|
|
Packit |
c32a2d |
module_path = compat_catpath(dir, module_file);
|
|
Packit |
c32a2d |
free(module_file);
|
|
Packit |
c32a2d |
if(!module_path)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
if(verbose > -1)
|
|
Packit |
c32a2d |
error("Failed to construct full path (out of memory?).");
|
|
Packit |
c32a2d |
return NULL;
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
if(verbose > 1)
|
|
Packit |
c32a2d |
fprintf(stderr, "Module path: %s\n", module_path );
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
/* Open the module */
|
|
Packit |
c32a2d |
handle = compat_dlopen(module_path);
|
|
Packit |
c32a2d |
free(module_path);
|
|
Packit |
c32a2d |
if (handle==NULL)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
if(verbose > -1)
|
|
Packit |
c32a2d |
error1("Failed to open module %s.", name);
|
|
Packit |
c32a2d |
return NULL;
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
/* Work out the symbol name */
|
|
Packit |
c32a2d |
module_symbol_len = strlen( MODULE_SYMBOL_PREFIX ) +
|
|
Packit |
c32a2d |
strlen( type ) +
|
|
Packit |
c32a2d |
strlen( MODULE_SYMBOL_SUFFIX ) + 1;
|
|
Packit |
c32a2d |
module_symbol = malloc(module_symbol_len);
|
|
Packit |
c32a2d |
if (module_symbol == NULL) {
|
|
Packit |
c32a2d |
if(verbose > -1)
|
|
Packit |
c32a2d |
error1( "Failed to allocate memory for module symbol: %s", strerror(errno) );
|
|
Packit |
c32a2d |
return NULL;
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
snprintf( module_symbol, module_symbol_len, "%s%s%s", MODULE_SYMBOL_PREFIX, type, MODULE_SYMBOL_SUFFIX );
|
|
Packit |
c32a2d |
debug1( "Module symbol: %s", module_symbol );
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
/* Get the information structure from the module */
|
|
Packit |
c32a2d |
module = (mpg123_module_t*)compat_dlsym(handle, module_symbol);
|
|
Packit |
c32a2d |
free( module_symbol );
|
|
Packit |
c32a2d |
if (module==NULL) {
|
|
Packit |
c32a2d |
if(verbose > -1)
|
|
Packit |
c32a2d |
error("Failed to get module symbol.");
|
|
Packit |
c32a2d |
return NULL;
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
/* Check the API version */
|
|
Packit |
c32a2d |
if (MPG123_MODULE_API_VERSION != module->api_version)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
if(verbose > -1)
|
|
Packit |
c32a2d |
error2( "API version of module does not match (got %i, expected %i).", module->api_version, MPG123_MODULE_API_VERSION);
|
|
Packit |
c32a2d |
compat_dlclose(handle);
|
|
Packit |
c32a2d |
return NULL;
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
/* Store handle in the data structure */
|
|
Packit |
c32a2d |
module->handle = handle;
|
|
Packit |
c32a2d |
return module;
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
/* Open a module, including directory search. */
|
|
Packit |
c32a2d |
mpg123_module_t* open_module( const char* type, const char* name, int verbose
|
|
Packit |
c32a2d |
, const char* bindir )
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
mpg123_module_t *module = NULL;
|
|
Packit |
c32a2d |
char *moddir = NULL;
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
moddir = get_module_dir(verbose, bindir);
|
|
Packit |
c32a2d |
if(!moddir)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
if(verbose > -1)
|
|
Packit |
c32a2d |
error("Failure getting module directory! (Perhaps set MPG123_MODDIR?)");
|
|
Packit |
c32a2d |
return NULL;
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
module = open_module_here(moddir, type, name, verbose);
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
free(moddir);
|
|
Packit |
c32a2d |
return module;
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
void close_module( mpg123_module_t* module, int verbose )
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
compat_dlclose(module->handle);
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
int list_modules( const char *type, char ***names, char ***descr, int verbose
|
|
Packit |
c32a2d |
, const char* bindir )
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
char *moddir = NULL;
|
|
Packit |
c32a2d |
int count = 0;
|
|
Packit |
c32a2d |
struct compat_dir *dir;
|
|
Packit |
c32a2d |
char *filename;
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
debug1("verbose:%i", verbose);
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
*names = NULL;
|
|
Packit |
c32a2d |
*descr = NULL;
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
moddir = get_module_dir(verbose, bindir);
|
|
Packit |
c32a2d |
if(moddir == NULL)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
if(verbose > -1)
|
|
Packit |
c32a2d |
error("Failure getting module directory! (Perhaps set MPG123_MODDIR?)");
|
|
Packit |
c32a2d |
return -1;
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
debug1("module dir: %s", moddir);
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
/* Open the module directory */
|
|
Packit |
c32a2d |
dir = compat_diropen(moddir);
|
|
Packit |
c32a2d |
if (dir==NULL) {
|
|
Packit |
c32a2d |
if(verbose > -1)
|
|
Packit |
c32a2d |
error2("Failed to open the module directory (%s): %s\n"
|
|
Packit |
c32a2d |
, moddir, strerror(errno));
|
|
Packit |
c32a2d |
free(moddir);
|
|
Packit |
c32a2d |
return -1;
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
while((filename=compat_nextfile(dir)))
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
/* Pointers to the pieces. */
|
|
Packit |
c32a2d |
char *module_name = NULL;
|
|
Packit |
c32a2d |
char *module_type = NULL;
|
|
Packit |
c32a2d |
char *uscore_pos = NULL;
|
|
Packit |
c32a2d |
mpg123_module_t *module = NULL;
|
|
Packit |
c32a2d |
char* ext;
|
|
Packit |
c32a2d |
size_t name_len;
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
/* Various checks as loop shortcuts, avoiding too much nesting. */
|
|
Packit |
c32a2d |
debug1("checking entry: %s", filename);
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
name_len = strlen(filename);
|
|
Packit |
c32a2d |
if(name_len < strlen(LT_MODULE_EXT))
|
|
Packit |
c32a2d |
goto list_modules_continue;
|
|
Packit |
c32a2d |
ext = filename
|
|
Packit |
c32a2d |
+ name_len
|
|
Packit |
c32a2d |
- strlen(LT_MODULE_EXT);
|
|
Packit |
c32a2d |
if(strcmp(ext, LT_MODULE_EXT))
|
|
Packit |
c32a2d |
goto list_modules_continue;
|
|
Packit |
c32a2d |
debug("has suffix");
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
/* Extract the module type and name */
|
|
Packit |
c32a2d |
uscore_pos = strchr( filename, '_' );
|
|
Packit |
c32a2d |
if( uscore_pos==NULL
|
|
Packit |
c32a2d |
|| (uscore_pos>=filename+name_len+1) )
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
debug("no underscore");
|
|
Packit |
c32a2d |
goto list_modules_continue;
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
*uscore_pos = '\0';
|
|
Packit |
c32a2d |
module_type = filename;
|
|
Packit |
c32a2d |
module_name = uscore_pos+1;
|
|
Packit |
c32a2d |
/* Only list modules of desired type. */
|
|
Packit |
c32a2d |
if(strcmp(type, module_type))
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
debug("wrong type");
|
|
Packit |
c32a2d |
goto list_modules_continue;
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
debug("has type");
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
/* Extract the short name of the module */
|
|
Packit |
c32a2d |
name_len -= uscore_pos - filename + 1;
|
|
Packit |
c32a2d |
if(name_len <= strlen(LT_MODULE_EXT))
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
debug("name too short");
|
|
Packit |
c32a2d |
goto list_modules_continue;
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
name_len -= strlen(LT_MODULE_EXT);
|
|
Packit |
c32a2d |
module_name[name_len] = '\0';
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
debug("opening module");
|
|
Packit |
c32a2d |
/* Open the module
|
|
Packit |
c32a2d |
Yes, this re-builds the file name we chopped to pieces just now. */
|
|
Packit |
c32a2d |
if((module=open_module_here(moddir, module_type, module_name, verbose)))
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
if( stringlists_add( names, descr
|
|
Packit |
c32a2d |
, module->name, module->description, &count) )
|
|
Packit |
c32a2d |
if(verbose > -1)
|
|
Packit |
c32a2d |
error("OOM");
|
|
Packit |
c32a2d |
/* Close the module again */
|
|
Packit |
c32a2d |
close_module(module, verbose);
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
list_modules_continue:
|
|
Packit |
c32a2d |
free(filename);
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
compat_dirclose(dir);
|
|
Packit |
c32a2d |
return count;
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
|