static PluginLib_c * LoadPluginLibrary ( const char * sLibName, CSphString & sError, bool bLinuxReload=false ) { CSphString sTmpfile; CSphString sLibfile; sLibfile.SetSprintf ( "%s/%s", g_sPluginDir.cstr(), sLibName ); // dlopen caches the old file content, even if file was updated // let's reload library from the temporary file to invalidate the cache if ( bLinuxReload ) { sTmpfile.SetSprintf ( "%s/%s.%u", g_sPluginDir.cstr(), sLibName, sphRand() ); if ( ::rename ( sLibfile.cstr(), sTmpfile.cstr() ) ) { sError.SetSprintf ( "failed to rename file (src=%s, dst=%s, errno=%d, error=%s)", sLibfile.cstr(), sTmpfile.cstr(), errno, strerror(errno) ); return NULL; } } void * pHandle = dlopen ( bLinuxReload ? sTmpfile.cstr() : sLibfile.cstr(), RTLD_LAZY | RTLD_LOCAL ); if ( !pHandle ) { const char * sDlerror = dlerror(); sError.SetSprintf ( "dlopen() failed: %s", sDlerror ? sDlerror : "(null)" ); return NULL; } sphLogDebug ( "dlopen(%s)=%p", bLinuxReload ? sTmpfile.cstr() : sLibfile.cstr(), pHandle ); // rename file back to the original name if ( bLinuxReload ) { if ( ::rename ( sTmpfile.cstr(), sLibfile.cstr() ) ) { sError.SetSprintf ( "failed to rename file (src=%s, dst=%s, errno=%d, error=%s)", sTmpfile.cstr(), sLibfile.cstr(), errno, strerror(errno) ); return NULL; } } CSphString sBasename = sLibName; const char * pDot = strchr ( sBasename.cstr(), '.' ); if ( pDot ) sBasename = sBasename.SubString ( 0, pDot-sBasename.cstr() ); CSphString sTmp; PluginVer_fn fnVer = (PluginVer_fn) dlsym ( pHandle, sTmp.SetSprintf ( "%s_ver", sBasename.cstr() ).cstr() ); if ( !fnVer ) { sError.SetSprintf ( "symbol '%s_ver' not found in '%s': update your UDF implementation", sBasename.cstr(), sLibName ); dlclose ( pHandle ); return NULL; } if ( fnVer() < SPH_UDF_VERSION ) { sError.SetSprintf ( "library '%s' was compiled using an older version of sphinxudf.h; it needs to be recompiled", sLibName ); dlclose ( pHandle ); return NULL; } return new PluginLib_c ( pHandle, sLibName ); }
bool sphConfTokenizer ( const CSphConfigSection & hIndex, CSphTokenizerSettings & tSettings, CSphString & sError ) { // charset_type CSphScopedPtr<ISphTokenizer> pTokenizer ( NULL ); if ( !hIndex("charset_type") || hIndex["charset_type"]=="sbcs" ) { tSettings.m_iType = TOKENIZER_SBCS; } else if ( hIndex["charset_type"]=="utf-8" ) { tSettings.m_iType = hIndex("ngram_chars") ? TOKENIZER_NGRAM : TOKENIZER_UTF8; } else { sError.SetSprintf ( "unknown charset type '%s'", hIndex["charset_type"].cstr() ); return false; } tSettings.m_sCaseFolding = hIndex.GetStr ( "charset_table" ); tSettings.m_iMinWordLen = Max ( hIndex.GetInt ( "min_word_len" ), 0 ); tSettings.m_sNgramChars = hIndex.GetStr ( "ngram_chars" ); tSettings.m_iNgramLen = Max ( hIndex.GetInt ( "ngram_len" ), 0 ); tSettings.m_sSynonymsFile = hIndex.GetStr ( "exceptions" ); // new option name if ( tSettings.m_sSynonymsFile.IsEmpty() ) tSettings.m_sSynonymsFile = hIndex.GetStr ( "synonyms" ); // deprecated option name tSettings.m_sIgnoreChars = hIndex.GetStr ( "ignore_chars" ); // phrase boundaries int iBoundaryStep = Max ( hIndex.GetInt ( "phrase_boundary_step" ), -1 ); if ( iBoundaryStep!=0 ) tSettings.m_sBoundary = hIndex.GetStr ( "phrase_boundary" ); return true; }
bool sphPluginDrop ( PluginType_e eType, const char * sName, CSphString & sError ) { #if !HAVE_DLOPEN sError = "no dlopen(), no plugins"; return false; #else CSphScopedLock<CSphMutex> tLock ( g_tPluginMutex ); PluginKey_t tKey ( eType, sName ); PluginDesc_c ** ppPlugin = g_hPlugins(tKey); if ( !ppPlugin || !*ppPlugin ) { sError.SetSprintf ( "plugin '%s' does not exist", sName ); return false; } PluginDesc_c * pPlugin = *ppPlugin; PluginLib_c * pLib = pPlugin->GetLib(); Verify ( g_hPlugins.Delete(tKey) ); pPlugin->Release(); if ( --pLib->m_iHashedPlugins==0 ) { g_hPluginLibs.Delete ( pLib->GetName() ); pLib->Release(); } return true; #endif // HAVE_DLOPEN }
static bool PluginLoadSymbols ( void * pDesc, const SymbolDesc_t * pSymbol, void * pHandle, const char * sName, CSphString & sError ) { #if !HAVE_DLOPEN sError = "no dlopen(), no plugins"; return false; #else CSphString s; while ( pSymbol->m_iOffsetOf>=0 ) { s.SetSprintf ( pSymbol->m_sPostfix[0] ? "%s_%s" : "%s%s", sName, pSymbol->m_sPostfix ); void ** ppFunc = (void**)((BYTE*)pDesc + pSymbol->m_iOffsetOf); *ppFunc = dlsym ( pHandle, s.cstr() ); if ( !*ppFunc && pSymbol->m_bRequired ) { sError.SetSprintf ( "symbol %s() not found", s.cstr() ); return false; } pSymbol++; } return true; #endif // HAVE_DLOPEN }
bool sphPluginReload ( const char * sName, CSphString & sError ) { #if !HAVE_DLOPEN sError = "no dlopen(), no plugins"; return false; #else // find all plugins from the given library CSphScopedLock<CSphMutex> tLock ( g_tPluginMutex ); CSphVector<PluginKey_t> dKeys; CSphVector<PluginDesc_c*> dPlugins; g_hPlugins.IterateStart(); while ( g_hPlugins.IterateNext() ) { PluginDesc_c * v = g_hPlugins.IterateGet(); if ( v->GetLibName()==sName ) { dKeys.Add ( g_hPlugins.IterateGetKey() ); dPlugins.Add ( g_hPlugins.IterateGet() ); } } // no plugins loaded? oops if ( dPlugins.GetLength()==0 ) { sError.SetSprintf ( "no active plugins loaded from %s", sName ); return false; } // load new library and check every plugin #if !USE_WINDOWS PluginLib_c * pNewLib = LoadPluginLibrary ( sName, sError, true ); #else PluginLib_c * pNewLib = LoadPluginLibrary ( sName, sError ); #endif if ( !pNewLib ) return false; // load all plugins CSphVector<PluginDesc_c*> dNewPlugins; ARRAY_FOREACH ( i, dPlugins ) { PluginDesc_c * pDesc = NULL; const SymbolDesc_t * pSym = NULL; switch ( dKeys[i].m_eType ) { case PLUGIN_RANKER: pDesc = new PluginRanker_c ( pNewLib ); pSym = g_dSymbolsRanker; break; case PLUGIN_INDEX_TOKEN_FILTER: pDesc = new PluginTokenFilter_c ( pNewLib ); pSym = g_dSymbolsTokenFilter; break; case PLUGIN_QUERY_TOKEN_FILTER: pDesc = new PluginQueryTokenFilter_c ( pNewLib ); pSym = g_dSymbolsQueryTokenFilter; break; case PLUGIN_FUNCTION: pDesc = new PluginUDF_c ( pNewLib, dPlugins[i]->GetUdfRetType() ); pSym = g_dSymbolsUDF; break; default: sphDie ( "INTERNAL ERROR: unknown plugin type %d in sphPluginReload()", (int)dKeys[i].m_eType ); return false; } if ( !PluginLoadSymbols ( pDesc, pSym, pNewLib->GetHandle(), dKeys[i].m_sName.cstr(), sError ) ) { pDesc->Release(); break; } dNewPlugins.Add ( pDesc ); }
bool sphPluginCreate ( const char * szLib, PluginType_e eType, const char * sName, ESphAttr eUDFRetType, CSphString & sError ) { #if !HAVE_DLOPEN sError = "no dlopen(), no plugins"; return false; #else if ( !g_bPluginsEnabled ) { sError = "plugin support disabled (requires a valid plugin_dir)"; return false; } // validate library name for ( const char * p = szLib; *p; p++ ) if ( *p=='/' || *p=='\\' ) { sError = "restricted character (path delimiter) in a library file name"; return false; } CSphString sLib = szLib; sLib.ToLower(); // FIXME? preregister known rankers instead? if ( eType==PLUGIN_RANKER ) { for ( int i=0; i<SPH_RANK_TOTAL; i++ ) { const char * r = sphGetRankerName ( ESphRankMode(i) ); if ( r && strcasecmp ( sName, r )==0 ) { sError.SetSprintf ( "%s is a reserved ranker name", r ); return false; } } } // from here, we need a lock (we intend to update the plugin hash) CSphScopedLock<CSphMutex> tLock ( g_tPluginMutex ); // validate function name PluginKey_t k ( eType, sName ); if ( g_hPlugins(k) ) { sError.SetSprintf ( "plugin '%s' already exists", k.m_sName.cstr() ); return false; } // lookup or load library PluginLib_c * pLib = NULL; if ( g_hPluginLibs ( sLib ) ) { pLib = g_hPluginLibs [ sLib ]; pLib->AddRef(); } else { pLib = LoadPluginLibrary ( sLib.cstr(), sError ); if ( !pLib ) return false; } assert ( pLib->GetHandle() ); PluginDesc_c * pPlugin = NULL; const SymbolDesc_t * pSym = NULL; switch ( eType ) { case PLUGIN_RANKER: pPlugin = new PluginRanker_c ( pLib ); pSym = g_dSymbolsRanker; break; case PLUGIN_INDEX_TOKEN_FILTER: pPlugin = new PluginTokenFilter_c ( pLib ); pSym = g_dSymbolsTokenFilter; break; case PLUGIN_QUERY_TOKEN_FILTER: pPlugin = new PluginQueryTokenFilter_c ( pLib ); pSym = g_dSymbolsQueryTokenFilter; break; case PLUGIN_FUNCTION: pPlugin = new PluginUDF_c ( pLib, eUDFRetType ); pSym = g_dSymbolsUDF; break; default: sError.SetSprintf ( "INTERNAL ERROR: unknown plugin type %d in CreatePlugin()", (int)eType ); pLib->Release(); return false; } // release the refcount that this very function is holding // or in other words, transfer the refcount to newly created plugin instance (it does its own addref) pLib->Release(); if ( !PluginLoadSymbols ( pPlugin, pSym, pLib->GetHandle(), k.m_sName.cstr(), sError ) ) { sError.SetSprintf ( "%s in %s", sError.cstr(), sLib.cstr() ); pPlugin->Release(); return false; } // add library if needed if ( !g_hPluginLibs ( sLib ) ) { Verify ( g_hPluginLibs.Add ( pLib, pLib->GetName() ) ); pLib->AddRef(); // the hash reference } // add function Verify ( g_hPlugins.Add ( pPlugin, k ) ); pPlugin->GetLib()->m_iHashedPlugins++; return true; #endif // HAVE_DLOPEN }