Blame src/libout123/module.c

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