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