/* * tcmodule.c -- transcode module system, take two. * (C) 2005-2010 - Francesco Romani * * 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 . */ #include "config.h" #ifdef HAVE_DLFCN_H #include #else # ifdef OS_DARWIN # include "libdldarwin/dlfcn.h" # endif #endif #include #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 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: */