summaryrefslogtreecommitdiffstats
path: root/debian/transcode/transcode-1.1.7/libtc/tcmodule.c
diff options
context:
space:
mode:
Diffstat (limited to 'debian/transcode/transcode-1.1.7/libtc/tcmodule.c')
-rw-r--r--debian/transcode/transcode-1.1.7/libtc/tcmodule.c1015
1 files changed, 1015 insertions, 0 deletions
diff --git a/debian/transcode/transcode-1.1.7/libtc/tcmodule.c b/debian/transcode/transcode-1.1.7/libtc/tcmodule.c
new file mode 100644
index 00000000..ca2d59ff
--- /dev/null
+++ b/debian/transcode/transcode-1.1.7/libtc/tcmodule.c
@@ -0,0 +1,1015 @@
+/*
+ * 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:
+ */