You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1016 lines
30 KiB
C

/*
* tcmodule.c -- transcode module system, take two.
* (C) 2005-2010 - Francesco Romani <fromani -at- gmail -dot- com>
*
* This file is part of transcode, a video stream processing tool.
*
* transcode is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* transcode is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "config.h"
#ifdef HAVE_DLFCN_H
#include <dlfcn.h>
#else
# ifdef OS_DARWIN
# include "libdldarwin/dlfcn.h"
# endif
#endif
#include <string.h>
#include "libtc.h"
#include "tccodecs.h"
#include "tcmodule-data.h"
#include "tcmodule-core.h"
#include "transcode.h"
#define TC_FACTORY_MAX_HANDLERS (16)
#define MOD_TYPE_MAX_LEN (TC_BUF_MIN * 2)
#define tc_module_init(module, features) \
(module)->klass->init(&((module)->instance), features)
#define tc_module_fini(module) \
(module)->klass->fini(&((module)->instance))
/* module entry point */
typedef const TCModuleClass* (*TCModuleEntry)(void);
typedef enum {
TC_DESCRIPTOR_FREE = 0, /* free to use */
TC_DESCRIPTOR_CREATED, /* reserved, but not yet registered */
TC_DESCRIPTOR_DONE, /* ok, all donw and ready to run */
} TCHandleStatus;
typedef struct tcmoduledescriptor_ TCModuleDescriptor;
struct tcmoduledescriptor_ {
const char *type; /* packed class + name using make_modtype below */
void *so_handle; /* used by dl*() stuff */
TCHandleStatus status;
TCModuleInfo info;
/* main copy of module class data.
* all instance pointers will refer to this. */
TCModuleClass klass;
int ref_count; /* how many instances are floating around? */
};
struct tcfactory_ {
const char *mod_path; /* base directory for plugin search */
int verbose;
TCModuleDescriptor descriptors[TC_FACTORY_MAX_HANDLERS];
int descriptor_count;
int instance_count;
};
/*************************************************************************
* dummy/fake default module class. Always fails complaining loudly. *
* Using this as default, every module class has already valid *
* (but sometimes useless) pointers to every method. *
*************************************************************************/
#define DUMMY_HEAVY_CHECK(self, method_name) do { \
if (self != NULL) { \
tc_log_warn(self->type, "critical: module doesn't provide" \
" %s method", method_name); \
} else { \
tc_log_error(__FILE__, "critical: %s method missing AND bad" \
" instance pointer", method_name); \
} \
} while (0)
#define DUMMY_CHECK(self, method_name) do { \
if (self != NULL) { \
tc_log_warn(self->type, \
"module doesn't provide %s method", method_name); \
} else { \
tc_log_error(__FILE__, "%s method missing AND bad" \
" instance pointer", method_name); \
} \
} while (0)
static int dummy_init(TCModuleInstance *self, uint32_t features)
{
DUMMY_HEAVY_CHECK(self, "initialization");
return TC_ERROR;
}
static int dummy_fini(TCModuleInstance *self)
{
DUMMY_HEAVY_CHECK(self, "finalization");
return TC_ERROR;
}
static int dummy_configure(TCModuleInstance *self,
const char *options, vob_t *vob)
{
DUMMY_HEAVY_CHECK(self, "configuration");
return TC_ERROR;
}
static int dummy_stop(TCModuleInstance *self)
{
DUMMY_HEAVY_CHECK(self, "stopping");
return TC_ERROR;
}
static int dummy_inspect(TCModuleInstance *self,
const char *param, const char **value)
{
DUMMY_HEAVY_CHECK(self, "inspect");
return TC_ERROR;
}
static int dummy_encode_video(TCModuleInstance *self,
vframe_list_t *inframe,
vframe_list_t *outframe)
{
DUMMY_CHECK(self, "encode_video");
return TC_ERROR;
}
static int dummy_encode_audio(TCModuleInstance *self,
aframe_list_t *inframe,
aframe_list_t *outframe)
{
DUMMY_CHECK(self, "encode_audio");
return TC_ERROR;
}
static int dummy_decode_video(TCModuleInstance *self,
vframe_list_t *inframe,
vframe_list_t *outframe)
{
DUMMY_CHECK(self, "decode_video");
return TC_ERROR;
}
static int dummy_decode_audio(TCModuleInstance *self,
aframe_list_t *inframe,
aframe_list_t *outframe)
{
DUMMY_CHECK(self, "decode_audio");
return TC_ERROR;
}
static int dummy_filter_video(TCModuleInstance *self,
vframe_list_t *frame)
{
DUMMY_CHECK(self, "filter_video");
return TC_ERROR;
}
static int dummy_filter_audio(TCModuleInstance *self,
aframe_list_t *frame)
{
DUMMY_CHECK(self, "filter_audio");
return TC_ERROR;
}
static int dummy_multiplex(TCModuleInstance *self,
vframe_list_t *vframe, aframe_list_t *aframe)
{
DUMMY_CHECK(self, "multiplex");
return TC_ERROR;
}
static int dummy_demultiplex(TCModuleInstance *self,
vframe_list_t *vframe, aframe_list_t *aframe)
{
DUMMY_CHECK(self, "demultiplex");
return TC_ERROR;
}
#undef DUMMY_HEAVY_CHECK
#undef DUMMY_CHECK
static const TCCodecID dummy_codecs_in[] = {
TC_CODEC_ANY, TC_CODEC_ERROR
};
static const TCCodecID dummy_codecs_out[] = {
TC_CODEC_ANY, TC_CODEC_ERROR
};
static const TCFormatID dummy_formats_in[] = {
TC_FORMAT_RAW, TC_FORMAT_ERROR
};
static const TCFormatID dummy_formats_out[] = {
TC_FORMAT_RAW, TC_FORMAT_ERROR
};
static TCModuleInfo dummy_info = {
.features = TC_MODULE_FEATURE_NONE,
.flags = TC_MODULE_FLAG_NONE,
.name = "dummy",
.version = "internal fake module class",
.description = "can't do anyhing",
.codecs_in = dummy_codecs_in,
.codecs_out = dummy_codecs_out,
.formats_in = dummy_formats_in,
.formats_out = dummy_formats_out
};
static const TCModuleClass dummy_class = {
.id = 0,
.info = &dummy_info,
.init = dummy_init,
.fini = dummy_fini,
.configure = dummy_configure,
.inspect = dummy_inspect,
.stop = dummy_stop,
.encode_audio = dummy_encode_audio,
.encode_video = dummy_encode_video,
.decode_audio = dummy_decode_audio,
.decode_video = dummy_decode_video,
.filter_audio = dummy_filter_audio,
.filter_video = dummy_filter_video,
.multiplex = dummy_multiplex,
.demultiplex = dummy_demultiplex
};
/*************************************************************************
* private helpers *
*************************************************************************/
/*
* translate_modclass:
* validate a module class name, represented by a given string.
*
* Parameters:
* modclass: a class nome to validate.
* Return Value:
* TC_MOFULE_FEATURE_* correspondent.
*/
static uint32_t translate_modclass(const char *modclass)
{
uint32_t ret = TC_MODULE_FEATURE_NONE;
if (modclass != NULL) {
if (!strcmp(modclass, "filter")) {
ret = TC_MODULE_FEATURE_FILTER;
} else if (!strcmp(modclass, "demultiplex")
|| !strcmp(modclass, "demux")) {
ret = TC_MODULE_FEATURE_DEMULTIPLEX;
} else if (!strcmp(modclass, "decode")) {
ret = TC_MODULE_FEATURE_DECODE;
} else if (!strcmp(modclass, "encode")) {
ret = TC_MODULE_FEATURE_ENCODE;
} else if (!strcmp(modclass, "multiplex")
|| !strcmp(modclass, "mplex")) {
ret = TC_MODULE_FEATURE_MULTIPLEX;
}
}
return ret;
}
/* FIXME: writeme */
static uint32_t translate_media(int media)
{
uint32_t ret = TC_MODULE_FEATURE_NONE;
switch (media) {
case TC_VIDEO:
ret = TC_MODULE_FEATURE_VIDEO;
break;
case TC_AUDIO:
ret = TC_MODULE_FEATURE_AUDIO;
break;
case TC_EXTRA:
ret = TC_MODULE_FEATURE_EXTRA;
break;
}
return ret;
}
/*
* TCModuleDescriptorIter:
* generic iterator function on factory descriptors.
* In some different contexts, a iterator can be applied on all module
* descriptors in a given factory. Specific iterator functions can do
* arbitrary actions on descriptor data.
* See below to get some usage examples.
*
* Parameters:
* desc: pointer to a TCModuleDescriptor.
* userdata: opaque pointer to function-specific data.
* Return Value:
* 0 -> keep on going
* <0 -> stop iteration and return code verbatim
* >0 -> stop iteration and return current iteration index
* Side effects:
* Arbitrary, defined by specific function.
* Preconditions:
* given factory (but isn't guaranteed that also descriptors are) already
* initialized and contains valid data.
*/
typedef int (*TCModuleDescriptorIter)(TCModuleDescriptor *desc, void *userdata);
/*
* tc_foreach_descriptor:
* apply given iterator with given data to all internal descriptors,
* *both used and unused*.
*
* Parameters:
* factory: factory instance to use
* iterator: iterator to apply at factory descriptors
* userdata: opaque data to pass to iterator along with each descriptor
* index: pointer to an integer. If not NULL, will be filled
* with index of last descriptor elaborated
* Return Value:
* return code of the last execution of iterator.
* Side effects:
* None (see specific descriptor for this).
* Postconditions:
* If return value is 0, given iteratr wass applied to *all*
* descriptors in factory.
*/
static int tc_foreach_descriptor(TCFactory factory,
TCModuleDescriptorIter iterator,
void *userdata,
int *index)
{
int ret, i = 0;
if (!factory || !iterator) {
return -1;
}
for (i = 0; i < TC_FACTORY_MAX_HANDLERS; i++) {
ret = iterator(&(factory->descriptors[i]), userdata);
if (ret != 0) {
break;
}
}
/* iteration stopped, so we mark the interruption point */
if (ret != 0 && index != NULL) {
*index = i;
}
return ret;
}
/*
* descriptor_something: some iterator functions
*/
/*
* descriptor_match_modtype:
* verify the match for a given descriptor and a given module type.
*
* Parameters:
* desc: descriptor to verify
* modtype_: module type to look for.
* Return Value:
* 1 if given descriptor has given module type,
* 0 succesfull.
* -1 if a given parameter is bogus.
*/
static int descriptor_match_modtype(TCModuleDescriptor *desc,
void *modtype_)
{
char *modtype = modtype_;
if (!desc || !modtype) {
return -1;
}
if (desc->status == TC_DESCRIPTOR_DONE
&& desc->type != NULL
&& (strcmp(desc->type, modtype) == 0)) {
/* found it! */
return 1;
}
return 0;
}
/*
* descriptor_is_free:
* verify the match for a given descriptor is an unitialized one.
*
* Parameters:
* desc: descriptor to verify
* unused: dummy parameter to achieve API conformancy.
* Return Value:
* 1 if given descriptor is a free one (uninitialized),
* 0 succesfull.
* -1 if a given parameter is bogus.
*/
static int descriptor_is_free(TCModuleDescriptor *desc, void *unused)
{
if (!desc) {
return -1;
}
if (desc->status == TC_DESCRIPTOR_FREE) {
return 1;
}
return 0;
}
/*
* descriptor_init:
* initialize a plugin descriptor with valid defaults.
*
* Parameters:
* desc: descriptor to initialize.
* unused: dummy parameter to achieve API conformancy.
* Return Value:
* 0 succesfull.
* -1 if a given parameter is bogus.
*/
static int descriptor_init(TCModuleDescriptor *desc, void *unused)
{
if (!desc) {
return -1;
}
desc->status = TC_DESCRIPTOR_FREE;
memcpy(&(desc->info), &dummy_info, sizeof(TCModuleInfo));
desc->klass.info = &(desc->info);
desc->type = NULL;
desc->so_handle = NULL;
desc->ref_count = 0;
return 0;
}
/*
* descriptor_fini:
* finalize a plugin descriptor, releasing all acquired
* resources.
*
* Parameters:
* desc: descriptor to finalize.
* unused: dummy parameter to achieve API conformancy.
* Return Value:
* 1 if given descriptor has still some live instances around,
* 0 succesfull.
* -1 if a given parameter is bogus.
* Side effects:
* A plugin will be released and unloaded (via dlclose()).
*/
static int descriptor_fini(TCModuleDescriptor *desc, void *unused)
{
if (!desc) {
return -1;
}
/* can't finalize an descriptor with some living instances still around */
if (desc->ref_count > 0) {
return 1;
}
if (desc->status == TC_DESCRIPTOR_DONE) {
if (desc->type != NULL) {
tc_free((void*)desc->type); /* avoid const warning */
desc->type = NULL;
}
if (desc->so_handle != NULL) {
dlclose(desc->so_handle);
desc->so_handle = NULL;
}
desc->status = TC_DESCRIPTOR_FREE;
}
return 0;
}
/* just a thin wrapper to adapt API */
static int find_by_modtype(TCFactory factory, const char *modtype)
{
int ret, id;
ret = tc_foreach_descriptor(factory, descriptor_match_modtype,
(void*)modtype, &id);
/* ret >= 1 -> found something */
return (ret >= 1) ?id : -1;
}
/* just a thin wrapper to adapt API */
static int find_first_free_descriptor(TCFactory factory)
{
int ret, id;
ret = tc_foreach_descriptor(factory, descriptor_is_free, NULL, &id);
/* ret >= 1 -> found something */
return (ret >= 1) ?id : -1;
}
/* Yeah, is that simple. Yet. ;) */
static void make_modtype(char *buf, size_t bufsize,
const char *modclass, const char *modname)
{
tc_snprintf(buf, bufsize, "%s:%s", modclass, modname);
}
/*
* tc_module_class_copy:
* copy a module class into another one. Can perform
* a soft (reference) copy or a hard (full) one.
* Only non-null function pointer to plugin operations
* will be copied.
* soft copy: make the two classes points to same real data.
* hard copy: make two independent copies duplicating the data.
*
* Parameters:
* klass: source class to be copied.
* nklass: class destionation of copy.
* soft_copy: boolean flag: if !0 do a soft copy,
* do an hard one otherwise.
* Return Value:
* 0 successfull
* -1 given (at least) a bad TCModuleClass reference
* 1 not enough memory to perform a full copy
* Postconditions:
* destination class is a copy of source class.
*/
#define COPY_IF_NOT_NULL(field) do { \
if (klass->field != NULL) { \
nklass->field = klass->field; \
} \
} while (0)
static int tc_module_class_copy(const TCModuleClass *klass,
TCModuleClass *nklass)
{
if (!klass || !nklass) {
/* 'impossible' condition */
tc_log_error(__FILE__, "bad module class reference for setup: %s%s",
(!klass) ?"plugin class" :"",
(!nklass) ?"core class" :"");
return -1;
}
if (!klass->init || !klass->fini || !klass->configure || !klass->stop
|| !klass->inspect) {
/* should'nt happen */
tc_log_error(__FILE__, "can't setup a module class without "
"one or more mandatory methods");
return -1;
}
/* register only method really provided by given class */
nklass->init = klass->init;
nklass->fini = klass->fini;
nklass->configure = klass->configure;
nklass->stop = klass->stop;
nklass->inspect = klass->inspect;
nklass->info = klass->info;
COPY_IF_NOT_NULL(encode_audio);
COPY_IF_NOT_NULL(encode_video);
COPY_IF_NOT_NULL(decode_audio);
COPY_IF_NOT_NULL(decode_video);
COPY_IF_NOT_NULL(filter_audio);
COPY_IF_NOT_NULL(filter_video);
COPY_IF_NOT_NULL(multiplex);
COPY_IF_NOT_NULL(demultiplex);
return 0;
}
#undef COPY_IF_NOT_NULL
/*************************************************************************
* module versioning helpers *
*************************************************************************/
struct tcmodver {
int reserved;
int major;
int minor;
int micro;
};
static void expand_version(uint32_t version, struct tcmodver *modver)
{
modver->reserved = (version & 0xFF000000) >> 24;
modver->major = (version & 0x00FF0000) >> 16;
modver->minor = (version & 0x0000FF00) >> 8;
modver->micro = (version & 0x000000FF);
}
/*
* tc_module_version_matches:
* check compatibilty between the transcode core version and
* a supplied module version.
* Only a major version mismatch gives incompatibility (...yet).
*
* Parameters:
* modversion: the module version being checked.
* Return Value:
* 0 if module is incompatible with transcode core
* !0 otherwise.
* Side Effects:
* in case of incompatibilty (of any degree), messages are
* tc_log()'d out.
*/
static int tc_module_version_matches(uint32_t modversion)
{
struct tcmodver ver_core, ver_mod;
expand_version(TC_MODULE_VERSION, &ver_core);
expand_version(modversion, &ver_mod);
if (ver_core.reserved != ver_mod.reserved) {
tc_log_error(__FILE__, "internal version error");
return 0;
}
/* different major versions are a no-no */
if (ver_core.major != ver_mod.major) {
tc_log_error(__FILE__, "incompatible module version "
"(core=%i.%i.%i|module=%i.%i.%i)",
ver_core.major, ver_core.minor, ver_core.micro,
ver_mod.major, ver_mod.minor, ver_mod.micro);
return 0;
}
/* if you use different minor version, you've to know that */
if (ver_core.minor != ver_mod.minor) {
tc_log_error(__FILE__, "old module version "
"(core=%i.%i.%i|module=%i.%i.%i)",
ver_core.major, ver_core.minor, ver_core.micro,
ver_mod.major, ver_mod.minor, ver_mod.micro);
/* still compatible! */
}
/* different micro version are ok'd silently */
return 1;
}
/*************************************************************************
* main private helpers: _load and _unload *
*************************************************************************/
#define RETURN_IF_INVALID_STRING(str, msg, errval) do { \
if (!str || !strlen(str)) { \
tc_log_error(__FILE__, msg); \
return (errval); \
} \
} while (0)
#define RETURN_IF_INVALID_QUIET(val, errval) do { \
if (!(val)) { \
return (errval); \
} \
} while (0)
#define TC_LOG_DEBUG(fp, level, format, ...) do { \
if ((fp)->verbose >= level) { \
tc_log_info(__FILE__, format, __VA_ARGS__); \
} \
} while (0)
/*
* tc_load_module:
* load in a given factory a plugin needed for a given module.
* please note that here 'plugin' and 'module' terms are used
* interchangeably since a given module name from a given module
* class usually (almost always, even if such constraint doesn't
* exist) originates from a plugin with same class and same name.
*
* In other words, doesn't exist (yet, nor is planned) a plugin
* that can generate more than one module and/or more than one
* module class
*
* Parameters:
* factory: module factory to loads module in
* modclass: class of plugin to load
* modname: name of plugin to load
* Return Value:
* >= 0 identifier (slot) of newly loaded plugin
* -1 error occcurred (and notified via tc_log*())
* Side effects:
* a plugin (.so) is loaded into process
* Preconditions:
* none.
* Postconditions:
* none
*/
static int tc_load_module(TCFactory factory,
const char *modclass, const char *modname)
{
int id = -1, ret = -1;
char full_modpath[PATH_MAX];
char modtype[MOD_TYPE_MAX_LEN];
TCModuleEntry modentry = NULL;
TCModuleDescriptor *desc = NULL;
const TCModuleClass *nclass;
/* 'impossible' conditions */
RETURN_IF_INVALID_STRING(modclass, "empty module class", -1);
RETURN_IF_INVALID_STRING(modname, "empty module name", -1);
make_modtype(modtype, PATH_MAX, modclass, modname);
tc_snprintf(full_modpath, PATH_MAX, "%s/%s_%s.so",
factory->mod_path, modclass, modname);
id = find_first_free_descriptor(factory);
if (id == -1) {
/* should'nt happen */
tc_log_error(__FILE__, "already loaded the maximum number "
"of modules (%i)", TC_FACTORY_MAX_HANDLERS);
return -1;
}
TC_LOG_DEBUG(factory, TC_DEBUG, "using slot %i for plugin '%s'",
id, modtype);
desc = &(factory->descriptors[id]);
desc->ref_count = 0;
desc->so_handle = dlopen(full_modpath, RTLD_GLOBAL | RTLD_NOW);
if (!desc->so_handle) {
TC_LOG_DEBUG(factory, TC_INFO, "can't load module '%s'; reason: %s",
modtype, dlerror());
goto failed_dlopen;
}
desc->type = tc_strdup(modtype);
if (!desc->type) {
goto failed_strdup;
}
desc->status = TC_DESCRIPTOR_CREATED;
/* soft copy is enough here, since information will be overwritten */
tc_module_class_copy(&dummy_class, &(desc->klass));
modentry = dlsym(desc->so_handle, "tc_plugin_setup");
if (!modentry) {
TC_LOG_DEBUG(factory, TC_INFO, "module '%s' doesn't have new style"
" entry point", modtype);
goto failed_setup;
}
nclass = modentry();
if (!tc_module_version_matches(nclass->version)) {
/* reason already tc_log'd out */
goto failed_setup;
}
ret = tc_module_class_copy(nclass, &(desc->klass));
if (ret != 0) {
/* should'nt happen */
tc_log_error(__FILE__, "failed class registration for module '%s'",
modtype);
goto failed_setup;
}
desc->klass.id = id; /* enforce class/descriptor id */
desc->status = TC_DESCRIPTOR_DONE;
factory->descriptor_count++;
return id;
failed_setup:
desc->status = TC_DESCRIPTOR_FREE;
tc_free((void*)desc->type); /* avoid const warning */
failed_strdup:
dlclose(desc->so_handle);
failed_dlopen:
return -1;
}
#define CHECK_VALID_ID(id, where) do { \
if (id < 0 || id > TC_FACTORY_MAX_HANDLERS) { \
if (factory->verbose >= TC_DEBUG) { \
tc_log_error(__FILE__, "%s: invalid id (%i)", where, id); \
} \
return -1; \
} \
} while (0)
/*
* tc_unload_module:
* unload a given (by id) plugin from given factory.
* This means that module belonging to such plugin is no longer
* avalaible from given factory, unless, of course, reloading such
* plugin.
*
* Parameters:
* factory: a module factory
* id: id of plugin to unload
* Return Value:
* 0 plugin unloaded correctly
* != 0 error occcurred (and notified via tc_log*())
* Side effects:
* a plugin (.so) is UNloaded from process
* Preconditions:
* reference count for given plugin is zero.
* This means that no modules instances created by such plugin are
* still active.
* Postconditions:
* none
*/
static int tc_unload_module(TCFactory factory, int id)
{
int ret = 0;
TCModuleDescriptor *desc = NULL;
CHECK_VALID_ID(id, "tc_unload_module");
desc = &(factory->descriptors[id]);
if (desc->ref_count > 0) {
TC_LOG_DEBUG(factory, TC_DEBUG, "can't unload a module with active"
" ref_count (id=%i, ref_count=%i)",
desc->klass.id, desc->ref_count);
return 1;
}
ret = descriptor_fini(desc, NULL);
if (ret == 0) {
factory->descriptor_count--;
return 0;
}
return ret;
}
/*************************************************************************
* implementation of exported functions *
*************************************************************************/
TCFactory tc_new_module_factory(const char *modpath, int verbose)
{
TCFactory factory = NULL;
RETURN_IF_INVALID_STRING(modpath, "empty module path", NULL);
factory = tc_zalloc(sizeof(struct tcfactory_));
RETURN_IF_INVALID_QUIET(factory, NULL);
factory->mod_path = modpath;
factory->verbose = verbose;
factory->descriptor_count = 0;
factory->instance_count = 0;
tc_foreach_descriptor(factory, descriptor_init, NULL, NULL);
return factory;
}
int tc_del_module_factory(TCFactory factory)
{
RETURN_IF_INVALID_QUIET(factory, 1);
tc_foreach_descriptor(factory, descriptor_fini, NULL, NULL);
if (factory->descriptor_count > 0) {
/* should'nt happpen */
tc_log_warn(__FILE__, "left out %i module descriptors",
factory->descriptor_count);
return -1;
}
tc_free(factory);
return 0;
}
TCModule tc_new_module(TCFactory factory,
const char *modclass, const char *modname,
int media)
{
char modtype[MOD_TYPE_MAX_LEN];
uint32_t flags = translate_modclass(modclass);
int id = -1, ret;
TCModule module = NULL;
RETURN_IF_INVALID_QUIET(factory, NULL);
if (flags == TC_MODULE_FEATURE_NONE) {
TC_LOG_DEBUG(factory, TC_INFO, "unknown module class '%s'",
modclass);
return NULL;
}
make_modtype(modtype, MOD_TYPE_MAX_LEN, modclass, modname);
TC_LOG_DEBUG(factory, TC_DEBUG, "trying to load '%s'", modtype);
id = find_by_modtype(factory, modtype);
if (id == -1) {
/* module type not already known */
TC_LOG_DEBUG(factory, TC_STATS, "plugin not found for '%s',"
" loading...", modtype);
id = tc_load_module(factory, modclass, modname);
if (id == -1) {
/* load failed, give up */
return NULL;
}
}
TC_LOG_DEBUG(factory, TC_DEBUG, "module descriptor found: id %i", id);
module = tc_zalloc(sizeof(struct tcmodule_));
module->instance.type = factory->descriptors[id].type;
module->instance.id = factory->instance_count + 1;
module->klass = &(factory->descriptors[id].klass);
ret = tc_module_init(module, flags|translate_media(media));
if (ret != 0) {
TC_LOG_DEBUG(factory, TC_DEBUG, "initialization of '%s' failed"
" (code=%i)", modtype, ret);
tc_free(module);
return NULL;
}
factory->descriptors[id].ref_count++;
factory->instance_count++;
TC_LOG_DEBUG(factory, TC_DEBUG, "module created: type='%s'"
" instance id=(%i)", module->instance.type,
module->instance.id);
TC_LOG_DEBUG(factory, TC_STATS, "descriptor ref_count=(%i) instances"
" so far=(%i)", factory->descriptors[id].ref_count,
factory->instance_count);
return module;
}
int tc_del_module(TCFactory factory, TCModule module)
{
int ret = 0, id = -1;
RETURN_IF_INVALID_QUIET(factory, 1);
RETURN_IF_INVALID_QUIET(module, -1);
id = module->klass->id;
CHECK_VALID_ID(id, "tc_del_module");
ret = tc_module_fini(module);
if (ret != 0) {
TC_LOG_DEBUG(factory, TC_DEBUG, "finalization of '%s' failed"
" (code=%i)", module->instance.type, ret);
return ret;
}
tc_free(module);
factory->instance_count--;
factory->descriptors[id].ref_count--;
if (factory->descriptors[id].ref_count == 0) {
ret = tc_unload_module(factory, id);
}
return ret;
}
/*************************************************************************
* Debug helpers. *
*************************************************************************/
int tc_plugin_count(const TCFactory factory)
{
RETURN_IF_INVALID_QUIET(factory, -1);
return factory->descriptor_count;
}
int tc_instance_count(const TCFactory factory)
{
RETURN_IF_INVALID_QUIET(factory, -1);
return factory->instance_count;
}
#include <assert.h>
int tc_compare_modules(const TCModule amod, const TCModule bmod)
{
assert(amod != NULL && bmod != NULL);
if ((amod == bmod) || (amod->instance.id == bmod->instance.id)) {
return 1;
}
if (strcmp(amod->instance.type, bmod->instance.type) == 0) {
/* some internal sanity checks.
* assert()s are used here because those conditions
* WILL NOT *NEVER* BE FALSE!
* otherwise something _*really*_ evil is going on
*/
assert(amod->klass != NULL && bmod->klass != NULL);
assert(amod->klass == bmod->klass);
assert(amod->klass->id == bmod->klass->id);
assert(amod->klass->info == bmod->klass->info);
/* we should check method pointers as well? */
return 0;
}
return -1;
}
/*************************************************************************/
/*
* Local variables:
* c-file-style: "stroustrup"
* c-file-offsets: ((case-label . *) (statement-case-intro . *))
* indent-tabs-mode: nil
* End:
*
* vim: expandtab shiftwidth=4:
*/