/* * Copyright (C) 2002-2003 Fhg Fokus * Copyright (C) 2006 iptego GmbH * * This file is part of SEMS, a free SIP media server. * * SEMS 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. This program is released under * the GPL with the additional exemption that compiling, linking, * and/or using OpenSSL is allowed. * * For a license to use the SEMS software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * SEMS 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "AmPlugIn.h" #include "AmConfig.h" #include "AmApi.h" #include "AmUtils.h" #include "AmSdp.h" #include "AmSipDispatcher.h" //#include "AmServer.h" #include "amci/amci.h" #include "amci/codecs.h" #include "log.h" #include <sys/types.h> #include <dirent.h> #include <dlfcn.h> #include <string.h> #include <errno.h> #include <set> #include <vector> #include <algorithm> using std::set; static unsigned int pcm16_bytes2samples(long h_codec, unsigned int num_bytes) { return num_bytes / 2; } static unsigned int pcm16_samples2bytes(long h_codec, unsigned int num_samples) { return num_samples * 2; } static unsigned int tevent_bytes2samples(long h_codec, unsigned int num_bytes) { return num_bytes; } static unsigned int tevent_samples2bytes(long h_codec, unsigned int num_samples) { return num_samples; } amci_codec_t _codec_pcm16 = { CODEC_PCM16, NULL, NULL, NULL, NULL, NULL, pcm16_bytes2samples, pcm16_samples2bytes }; amci_codec_t _codec_tevent = { CODEC_TELEPHONE_EVENT, NULL, NULL, NULL, NULL, NULL, tevent_bytes2samples, tevent_samples2bytes }; amci_payload_t _payload_tevent = { -1, "telephone-event", 8000, // telephone-event has always SR 8000 -1, CODEC_TELEPHONE_EVENT, -1 }; AmPlugIn* AmPlugIn::_instance=0; AmPlugIn::AmPlugIn() : dynamic_pl(DYNAMIC_PAYLOAD_TYPE_START) //ctrlIface(NULL) { } static std::set<AmPluginFactory*> deleted_factories; static std::set<string> deleted_factories_names; static void delete_plugin_factory(std::pair<string, AmPluginFactory*> pf) { if ((deleted_factories.find(pf.second) == deleted_factories.end()) && (deleted_factories_names.find(pf.first) == deleted_factories_names.end())) { DBG("deleting plug-in factory: %s\n", pf.first.c_str()); deleted_factories.insert(pf.second); deleted_factories_names.insert(pf.first); delete pf.second; } } AmPlugIn::~AmPlugIn() { std::for_each(name2app.begin(), name2app.end(), delete_plugin_factory); std::for_each(name2seh.begin(), name2seh.end(), delete_plugin_factory); std::for_each(name2base.begin(), name2base.end(), delete_plugin_factory); std::for_each(name2di.begin(), name2di.end(), delete_plugin_factory); std::for_each(name2logfac.begin(), name2logfac.end(), delete_plugin_factory); for(vector<void*>::iterator it=dlls.begin();it!=dlls.end();++it) dlclose(*it); } void AmPlugIn::dispose() { if (_instance) { delete _instance; } } AmPlugIn* AmPlugIn::instance() { if(!_instance) _instance = new AmPlugIn(); return _instance; } void AmPlugIn::init() { vector<string> excluded_payloads_v = explode(AmConfig::ExcludePayloads, ";"); for (vector<string>::iterator it = excluded_payloads_v.begin(); it != excluded_payloads_v.end();it++) excluded_payloads.insert(*it); DBG("adding built-in codecs...\n"); addCodec(&_codec_pcm16); addCodec(&_codec_tevent); addPayload(&_payload_tevent); } int AmPlugIn::load(const string& directory, const string& plugins) { int err=0; if (!plugins.length()) { INFO("AmPlugIn: loading modules in directory '%s':\n", directory.c_str()); DIR* dir = opendir(directory.c_str()); if (!dir){ ERROR("while opening plug-in directory (%s): %s\n", directory.c_str(), strerror(errno)); return -1; } vector<string> excluded_plugins = explode(AmConfig::ExcludePlugins, ";"); set<string> excluded_plugins_s; for (vector<string>::iterator it = excluded_plugins.begin(); it != excluded_plugins.end();it++) excluded_plugins_s.insert(*it); struct dirent* entry; while( ((entry = readdir(dir)) != NULL) && (err == 0) ){ string plugin_name = string(entry->d_name); if(plugin_name.find(".so",plugin_name.length()-3) == string::npos ){ continue; } if (excluded_plugins_s.find(plugin_name.substr(0, plugin_name.length()-3)) != excluded_plugins_s.end()) { DBG("skipping excluded plugin %s\n", plugin_name.c_str()); continue; } string plugin_file = directory + "/" + plugin_name; DBG("loading %s ...\n",plugin_file.c_str()); if( (err = loadPlugIn(plugin_file)) < 0 ) { ERROR("while loading plug-in '%s'\n",plugin_file.c_str()); return -1; } } closedir(dir); } else { INFO("AmPlugIn: loading modules: '%s'\n", plugins.c_str()); vector<string> plugins_list = explode(plugins, ";"); for (vector<string>::iterator it = plugins_list.begin(); it != plugins_list.end(); it++) { string plugin_file = *it; if (plugin_file == "sipctrl") { WARN("sipctrl is integrated into the core, loading sipctrl " "module is not necessary any more\n"); WARN("please update your configuration to not load sipctrl module\n"); continue; } if(plugin_file.find(".so",plugin_file.length()-3) == string::npos ) plugin_file+=".so"; plugin_file = directory + "/" + plugin_file; DBG("loading %s...\n",plugin_file.c_str()); if( (err = loadPlugIn(plugin_file)) < 0 ) { ERROR("while loading plug-in '%s'\n",plugin_file.c_str()); // be strict here: if plugin not loaded, stop! return err; } } } DBG("AmPlugIn: modules loaded.\n"); std::map<string,AmSessionFactory*> app_load = name2app; set<string> loaded_modules; DBG("AmPlugIn: Initializing plugins...\n"); // initialize base components for(std::map<std::string,AmPluginFactory*>::iterator it = name2base.begin(); it != name2base.end(); it++){ if (loaded_modules.find(it->first) != loaded_modules.end()) continue; err = it->second->onLoad(); if(err) return err; loaded_modules.insert(it->first); } // initialize session event handlers for(std::map<std::string,AmSessionEventHandlerFactory*>::iterator it = name2seh.begin(); it != name2seh.end(); it++){ if (loaded_modules.find(it->first) != loaded_modules.end()) continue; err = it->second->onLoad(); if(err) return err; loaded_modules.insert(it->first); } // initialize DI component plugins for(std::map<std::string,AmDynInvokeFactory*>::iterator it = name2di.begin(); it != name2di.end(); it++){ if (loaded_modules.find(it->first) != loaded_modules.end()) continue; err = it->second->onLoad(); if(err) return err; loaded_modules.insert(it->first); } // init logging facilities for(std::map<std::string,AmLoggingFacility*>::iterator it = name2logfac.begin(); it != name2logfac.end(); it++){ if (!(loaded_modules.find(it->first) != loaded_modules.end())) { err = it->second->onLoad(); if(err) return err; loaded_modules.insert(it->first); } // register for receiving logging messages register_log_hook(it->second); } // application plugins for(std::map<std::string,AmSessionFactory*>::iterator it = app_load.begin(); it != app_load.end(); it++){ if (loaded_modules.find(it->first) != loaded_modules.end()) continue; err = it->second->onLoad(); if(err) return err; loaded_modules.insert(it->first); } DBG("AmPlugIn: Initialized plugins.\n"); return 0; } int AmPlugIn::loadPlugIn(const string& file) { void* h_dl = dlopen(file.c_str(),RTLD_NOW | RTLD_GLOBAL); if(!h_dl){ ERROR("AmPlugIn::loadPlugIn: %s: %s\n",file.c_str(),dlerror()); return -1; } FactoryCreate fc = NULL; amci_exports_t* exports = (amci_exports_t*)dlsym(h_dl,"amci_exports"); bool has_sym=false; if(exports){ if(loadAudioPlugIn(exports)) goto error; goto end; } if((fc = (FactoryCreate)dlsym(h_dl,FACTORY_SESSION_EXPORT_STR)) != NULL){ if(loadAppPlugIn((AmPluginFactory*)fc())) goto error; has_sym=true; } if((fc = (FactoryCreate)dlsym(h_dl,FACTORY_SESSION_EVENT_HANDLER_EXPORT_STR)) != NULL){ if(loadSehPlugIn((AmPluginFactory*)fc())) goto error; has_sym=true; } if((fc = (FactoryCreate)dlsym(h_dl,FACTORY_PLUGIN_EXPORT_STR)) != NULL){ if(loadBasePlugIn((AmPluginFactory*)fc())) goto error; has_sym=true; } if((fc = (FactoryCreate)dlsym(h_dl,FACTORY_PLUGIN_CLASS_EXPORT_STR)) != NULL){ if(loadDiPlugIn((AmPluginFactory*)fc())) goto error; has_sym=true; } if((fc = (FactoryCreate)dlsym(h_dl,FACTORY_LOG_FACILITY_EXPORT_STR)) != NULL){ if(loadLogFacPlugIn((AmPluginFactory*)fc())) goto error; has_sym=true; } if(!has_sym){ ERROR("Plugin type could not be detected (%s)(%s)\n",file.c_str(),dlerror()); goto error; } end: dlls.push_back(h_dl); return 0; error: dlclose(h_dl); return -1; } amci_inoutfmt_t* AmPlugIn::fileFormat(const string& fmt_name, const string& ext) { if(!fmt_name.empty()){ std::map<std::string,amci_inoutfmt_t*>::iterator it = file_formats.find(fmt_name); if ((it != file_formats.end()) && (ext.empty() || (ext == it->second->ext))) return it->second; } else if(!ext.empty()){ std::map<std::string,amci_inoutfmt_t*>::iterator it = file_formats.begin(); for(;it != file_formats.end();++it){ if(ext == it->second->ext) return it->second; } } return 0; } amci_codec_t* AmPlugIn::codec(int id) { std::map<int,amci_codec_t*>::iterator it = codecs.find(id); if(it != codecs.end()) return it->second; return 0; } amci_payload_t* AmPlugIn::payload(int payload_id) { std::map<int,amci_payload_t*>::iterator it = payloads.find(payload_id); if(it != payloads.end()) return it->second; return 0; } int AmPlugIn::getDynPayload(const string& name, int rate, int encoding_param) { // find a dynamic payload by name/rate and encoding_param (channels, if > 0) for(std::map<int, amci_payload_t*>::const_iterator pl_it = payloads.begin(); pl_it != payloads.end(); ++pl_it) if( (name == pl_it->second->name) && (rate == pl_it->second->sample_rate) ) { if ((encoding_param > 0) && (pl_it->second->channels > 0) && (encoding_param != pl_it->second->channels)) continue; return pl_it->first; } // not found return -1; } amci_subtype_t* AmPlugIn::subtype(amci_inoutfmt_t* iofmt, int subtype) { if(!iofmt) return 0; amci_subtype_t* st = iofmt->subtypes; if(subtype<0) // default subtype wanted return st; for(;;st++){ if(!st || st->type<0) break; if(st->type == subtype) return st; } return 0; } int AmPlugIn::subtypeID(amci_inoutfmt_t* iofmt, const string& subtype_name) { if(!iofmt) return -1; amci_subtype_t* st = iofmt->subtypes; if(subtype_name.empty()) // default subtype wanted return st->type; for(;;st++){ if(!st || st->type<0) break; if(st->name == subtype_name) return st->type; } return -1; } AmSessionFactory* AmPlugIn::getFactory4App(const string& app_name) { AmSessionFactory* res = NULL; name2app_mut.lock(); std::map<std::string,AmSessionFactory*>::iterator it = name2app.find(app_name); if(it != name2app.end()) res = it->second; name2app_mut.unlock(); return res; } AmSessionEventHandlerFactory* AmPlugIn::getFactory4Seh(const string& name) { std::map<std::string,AmSessionEventHandlerFactory*>::iterator it = name2seh.find(name); if(it != name2seh.end()) return it->second; return 0; } AmDynInvokeFactory* AmPlugIn::getFactory4Di(const string& name) { std::map<std::string,AmDynInvokeFactory*>::iterator it = name2di.find(name); if(it != name2di.end()) return it->second; return 0; } AmLoggingFacility* AmPlugIn::getFactory4LogFaclty(const string& name) { std::map<std::string,AmLoggingFacility*>::iterator it = name2logfac.find(name); if(it != name2logfac.end()) return it->second; return 0; } int AmPlugIn::loadAudioPlugIn(amci_exports_t* exports) { if(!exports){ ERROR("audio plug-in doesn't contain any exports !\n"); return -1; } if (exports->module_load) { if (exports->module_load() < 0) { ERROR("initializing audio plug-in!\n"); return -1; } } for( amci_codec_t* c=exports->codecs; c->id>=0; c++ ){ if(addCodec(c)) goto error; } for( amci_payload_t* p=exports->payloads; p->name; p++ ){ if(addPayload(p)) goto error; } for(amci_inoutfmt_t* f = exports->file_formats; f->name; f++ ){ if(addFileFormat(f)) goto error; } return 0; error: return -1; } int AmPlugIn::loadAppPlugIn(AmPluginFactory* f) { AmSessionFactory* sf = dynamic_cast<AmSessionFactory*>(f); if(!sf){ ERROR("invalid application plug-in!\n"); return -1; } name2app_mut.lock(); if(name2app.find(sf->getName()) != name2app.end()){ ERROR("application '%s' already loaded !\n",sf->getName().c_str()); name2app_mut.unlock(); return -1; } name2app.insert(std::make_pair(sf->getName(),sf)); DBG("application '%s' loaded.\n",sf->getName().c_str()); name2app_mut.unlock(); return 0; } int AmPlugIn::loadSehPlugIn(AmPluginFactory* f) { AmSessionEventHandlerFactory* sf = dynamic_cast<AmSessionEventHandlerFactory*>(f); if(!sf){ ERROR("invalid session component plug-in!\n"); goto error; } if(name2seh.find(sf->getName()) != name2seh.end()){ ERROR("session component '%s' already loaded !\n",sf->getName().c_str()); goto error; } name2seh.insert(std::make_pair(sf->getName(),sf)); DBG("session component '%s' loaded.\n",sf->getName().c_str()); return 0; error: return -1; } int AmPlugIn::loadBasePlugIn(AmPluginFactory* f) { name2base.insert(std::make_pair(f->getName(),f)); return 0; } int AmPlugIn::loadDiPlugIn(AmPluginFactory* f) { AmDynInvokeFactory* sf = dynamic_cast<AmDynInvokeFactory*>(f); if(!sf){ ERROR("invalid component plug-in!\n"); goto error; } if(name2di.find(sf->getName()) != name2di.end()){ ERROR("component '%s' already loaded !\n",sf->getName().c_str()); goto error; } name2di.insert(std::make_pair(sf->getName(),sf)); DBG("component '%s' loaded.\n",sf->getName().c_str()); return 0; error: return -1; } int AmPlugIn::loadLogFacPlugIn(AmPluginFactory* f) { AmLoggingFacility* sf = dynamic_cast<AmLoggingFacility*>(f); if(!sf){ ERROR("invalid logging facility plug-in!\n"); goto error; } if(name2logfac.find(sf->getName()) != name2logfac.end()){ ERROR("logging facility '%s' already loaded !\n", sf->getName().c_str()); goto error; } name2logfac.insert(std::make_pair(sf->getName(),sf)); DBG("logging facility component '%s' loaded.\n",sf->getName().c_str()); return 0; error: return -1; } int AmPlugIn::addCodec(amci_codec_t* c) { if(codecs.find(c->id) != codecs.end()){ ERROR("codec id (%i) already supported\n",c->id); return -1; } codecs.insert(std::make_pair(c->id,c)); DBG("codec id %i inserted\n",c->id); return 0; } int AmPlugIn::addPayload(amci_payload_t* p) { if (excluded_payloads.find(p->name) != excluded_payloads.end()) { DBG("Not enabling excluded payload '%s'\n", p->name); return 0; } amci_codec_t* c; unsigned int i, id; if( !(c = codec(p->codec_id)) ){ ERROR("in payload '%s': codec id (%i) not supported\n", p->name, p->codec_id); return -1; } if(p->payload_id != -1){ if(payloads.find(p->payload_id) != payloads.end()){ ERROR("payload id (%i) already supported\n",p->payload_id); return -1; } payloads.insert(std::make_pair(p->payload_id,p)); id = p->payload_id; } else { payloads.insert(std::make_pair(dynamic_pl,p)); id = dynamic_pl; dynamic_pl++; } for (i = 0; i < AmConfig::CodecOrder.size(); i++) { if (p->name == AmConfig::CodecOrder[i]) break; } if (i >= AmConfig::CodecOrder.size()) { payload_order.insert(std::make_pair(id + 100, id)); DBG("payload '%s' inserted with id %i and order %i\n", p->name, id, id + 100); } else { payload_order.insert(std::make_pair(i, id)); DBG("payload '%s' inserted with id %i and order %i\n", p->name, id, i); } return 0; } int AmPlugIn::addFileFormat(amci_inoutfmt_t* f) { if(file_formats.find(f->name) != file_formats.end()){ ERROR("file format '%s' already supported\n",f->name); return -1; } amci_subtype_t* st = f->subtypes; for(; st->type >= 0; st++ ){ if( !codec(st->codec_id) ){ ERROR("in '%s' subtype %i: codec id (%i) not supported\n", f->name,st->type,st->codec_id); return -1; } if (st->sample_rate < 0) { ERROR("in '%s' subtype %i: rate must be specified!" " (ubr no longer supported)\n", f->name,st->type); return -1; } if (st->channels < 0) { ERROR("in '%s' subtype %i: channels must be specified!" "(unspecified channel count no longer supported)\n", f->name,st->type); return -1; } } DBG("file format %s inserted\n",f->name); file_formats.insert(std::make_pair(f->name,f)); return 0; } bool AmPlugIn::registerFactory4App(const string& app_name, AmSessionFactory* f) { bool res; name2app_mut.lock(); std::map<std::string,AmSessionFactory*>::iterator it = name2app.find(app_name); if(it != name2app.end()){ WARN("Application '%s' has already been registered and cannot be " "registered a second time\n", app_name.c_str()); res = false; } else { name2app.insert(make_pair(app_name,f)); res = true; } name2app_mut.unlock(); return res; } // static alias to registerFactory4App bool AmPlugIn::registerApplication(const string& app_name, AmSessionFactory* f) { bool res = instance()->registerFactory4App(app_name, f); if (res) { DBG("Application '%s' registered.\n", app_name.c_str()); } return res; } AmSessionFactory* AmPlugIn::findSessionFactory(AmSipRequest& req) { if(req.cmd.empty()){ ERROR("AmPlugIn::findSessionFactory: req.cmd is empty!\n"); return NULL; } else if(req.cmd == "sems"){ switch (AmConfig::AppSelect) { case AmConfig::App_RURIUSER: req.cmd = req.user; break; case AmConfig::App_APPHDR: req.cmd = getHeader(req.hdrs, APPNAME_HDR, true); break; case AmConfig::App_RURIPARAM: req.cmd = get_header_param(req.r_uri, "app"); break; case AmConfig::App_MAPPING: req.cmd = ""; // no match if not found run_regex_mapping(AmConfig::AppMapping, req.r_uri.c_str(), req.cmd); break; case AmConfig::App_SPECIFIED: req.cmd = AmConfig::Application; break; } if (req.cmd.empty()) { ERROR("could not find any application matching configured criteria\n"); return NULL; } } AmSessionFactory* session_factory = getFactory4App(req.cmd); if(!session_factory) { ERROR("AmPlugIn::findSessionFactory: application '%s' not found !\n", req.cmd.c_str()); } return session_factory; } #define REGISTER_STUFF(comp_name, map_name, param_name) \ if(instance()->map_name.find(param_name) != instance()->map_name.end()){ \ ERROR(comp_name "'%s' already registered !\n", param_name.c_str()); \ return false; \ } \ \ instance()->map_name.insert(std::make_pair(param_name,f)); \ DBG(comp_name " '%s' registered.\n",param_name.c_str()); \ return true; bool AmPlugIn::registerSIPEventHandler(const string& seh_name, AmSessionEventHandlerFactory* f) { REGISTER_STUFF("SIP Event handler", name2seh, seh_name); } bool AmPlugIn::registerDIInterface(const string& di_name, AmDynInvokeFactory* f) { REGISTER_STUFF("DI Interface", name2di, di_name); } bool AmPlugIn::registerLoggingFacility(const string& lf_name, AmLoggingFacility* f) { REGISTER_STUFF("Logging Facility", name2logfac, lf_name); } #undef REGISTER_STUFF