1 //
2 // $Id$
3 //
4 
5 //
6 // Copyright (c) 2001-2016, Andrew Aksyonoff
7 // Copyright (c) 2008-2016, Sphinx Technologies Inc
8 // All rights reserved
9 //
10 // This program is free software; you can redistribute it and/or modify
11 // it under the terms of the GNU General Public License. You should have
12 // received a copy of the GPL license along with this program; if you
13 // did not, you can find it at http://www.gnu.org/
14 //
15 
16 #include "sphinx.h"
17 #include "sphinxint.h"
18 #include "sphinxplugin.h"
19 
20 #if !USE_WINDOWS
21 #include <unistd.h>
22 #include <sys/time.h>
23 #ifdef HAVE_DLOPEN
24 #include <dlfcn.h>
25 #endif // HAVE_DLOPEN
26 #endif // !USE_WINDOWS
27 
28 #if !USE_WINDOWS
29 #ifndef HAVE_DLERROR
30 #define dlerror() ""
31 #endif // HAVE_DLERROR
32 #endif // !USE_WINDOWS
33 
34 //////////////////////////////////////////////////////////////////////////
35 // TYPES
36 //////////////////////////////////////////////////////////////////////////
37 
38 /// loaded plugin library
39 struct PluginLib_t
40 {
41 	void *				m_pHandle;				///< handle from dlopen()
42 	int					m_dCount[PLUGIN_TOTAL]; ///< per-type plugin counts from this library
43 	PluginReinit_fn		m_fnReinit;				///< per-library reinitialization func (for prefork), optional
44 };
45 
46 /// plugin key
47 struct PluginKey_t
48 {
49 	PluginType_e		m_eType;
50 	CSphString			m_sName;
51 
PluginKey_tPluginKey_t52 	PluginKey_t()
53 	{}
54 
PluginKey_tPluginKey_t55 	PluginKey_t ( PluginType_e eType, const char * sName )
56 		: m_eType ( eType )
57 		, m_sName ( sName )
58 	{
59 		m_sName.ToLower();
60 	}
61 
HashPluginKey_t62 	static int Hash ( const PluginKey_t & v )
63 	{
64 		return sphCRC32 ( v.m_sName.cstr(), v.m_sName.Length(),
65 			sphCRC32 ( &v.m_eType, sizeof(v.m_eType) ) );
66 	}
67 
operator ==PluginKey_t68 	bool operator == ( const PluginKey_t & rhs )
69 	{
70 		return m_eType==rhs.m_eType && m_sName==rhs.m_sName;
71 	}
72 };
73 
74 //////////////////////////////////////////////////////////////////////////
75 // GLOBALS
76 //////////////////////////////////////////////////////////////////////////
77 
78 const char * g_dPluginTypes[PLUGIN_TOTAL] = { "udf", "ranker", "index_token_filter", "query_token_filter" };
79 
80 //////////////////////////////////////////////////////////////////////////
81 
82 static bool								g_bPluginsEnabled = false;	///< is there any plugin support all?
83 static bool								g_bPluginsLocked = false;	///< do we allow CREATE/DROP at this point?
84 static CSphString						g_sPluginDir;
85 static CSphStaticMutex					g_tPluginMutex;				///< common plugin mutex (access to lib, func and ranker hashes)
86 static SmallStringHash_T<PluginLib_t>	g_hPluginLibs;				///< key is the filename (no path)
87 
88 static CSphOrderedHash<PluginDesc_c*, PluginKey_t, PluginKey_t, 256>	g_hPlugins;
89 
90 //////////////////////////////////////////////////////////////////////////
91 
Use() const92 void PluginDesc_c::Use() const
93 {
94 	g_tPluginMutex.Lock ();
95 	m_iUserCount++;
96 	g_tPluginMutex.Unlock ();
97 }
98 
99 
Release() const100 void PluginDesc_c::Release() const
101 {
102 	g_tPluginMutex.Lock ();
103 	m_iUserCount--;
104 	assert ( m_iUserCount>=0 );
105 	g_tPluginMutex.Unlock ();
106 }
107 
108 //////////////////////////////////////////////////////////////////////////
109 // PLUGIN MANAGER
110 //////////////////////////////////////////////////////////////////////////
111 
112 #if USE_WINDOWS
113 #define HAVE_DLOPEN		1
114 #define RTLD_LAZY		0
115 #define RTLD_LOCAL		0
116 
dlsym(void * lib,const char * name)117 void * dlsym ( void * lib, const char * name )
118 {
119 	return GetProcAddress ( (HMODULE)lib, name );
120 }
121 
dlopen(const char * libname,int)122 void * dlopen ( const char * libname, int )
123 {
124 	return LoadLibraryEx ( libname, NULL, 0 );
125 }
126 
dlclose(void * lib)127 int dlclose ( void * lib )
128 {
129 	return FreeLibrary ( (HMODULE)lib )
130 		? 0
131 		: GetLastError();
132 }
133 
dlerror()134 const char * dlerror()
135 {
136 	static char sError[256];
137 	DWORD uError = GetLastError();
138 	FormatMessage ( FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL,
139 		uError, LANG_SYSTEM_DEFAULT, (LPTSTR)sError, sizeof(sError), NULL );
140 	return sError;
141 }
142 #endif // USE_WINDOWS
143 
144 //////////////////////////////////////////////////////////////////////////
145 
sphPluginInit(const char * sDir)146 void sphPluginInit ( const char * sDir )
147 {
148 	if ( !sDir || !*sDir )
149 		return;
150 
151 	g_sPluginDir = sDir;
152 	g_bPluginsEnabled = true;
153 	g_bPluginsLocked = false;
154 }
155 
156 
sphPluginLock(bool bLocked)157 void sphPluginLock ( bool bLocked )
158 {
159 	g_bPluginsLocked = bLocked;
160 }
161 
162 
sphPluginParseSpec(const CSphString & sParams,CSphVector<CSphString> & dParams,CSphString & sError)163 bool sphPluginParseSpec ( const CSphString & sParams, CSphVector<CSphString> & dParams, CSphString & sError )
164 {
165 	dParams.Resize ( 0 );
166 	sphSplit ( dParams, sParams.cstr(), ":" );
167 
168 	switch ( dParams.GetLength() )
169 	{
170 	case 0:
171 		return true;
172 
173 	case 1:
174 		sError = "filter name required in spec string; example: \"plugins.so:myfilter\"";
175 		return false;
176 
177 	case 2:
178 		dParams.Add ( "" );
179 		return true;
180 
181 	case 3:
182 		return true;
183 	}
184 
185 	sError = "too many parts in spec string; must be in \"plugins.so:myfilter:options\" format";
186 	return false;
187 }
188 
189 struct SymbolDesc_t
190 {
191 	int				m_iOffsetOf;	///< pointer member location in the descriptor structure
192 	const char *	m_sPostfix;		///< symbol name postfix
193 	bool			m_bRequired;	///< whether this symbol must be present
194 };
195 
196 
PluginLoadSymbols(void * pDesc,const SymbolDesc_t * pSymbol,void * pHandle,const char * sName,CSphString & sError)197 static bool PluginLoadSymbols ( void * pDesc, const SymbolDesc_t * pSymbol, void * pHandle, const char * sName, CSphString & sError )
198 {
199 #if !HAVE_DLOPEN
200 	sError = "no dlopen(), no plugins";
201 	return false;
202 #else
203 	CSphString s;
204 	while ( pSymbol->m_iOffsetOf>=0 )
205 	{
206 		s.SetSprintf ( pSymbol->m_sPostfix[0] ? "%s_%s" : "%s%s", sName, pSymbol->m_sPostfix );
207 		void ** ppFunc = (void**)((BYTE*)pDesc + pSymbol->m_iOffsetOf);
208 		*ppFunc = dlsym ( pHandle, s.cstr() );
209 		if ( !*ppFunc && pSymbol->m_bRequired )
210 		{
211 			sError.SetSprintf ( "symbol %s() not found", s.cstr() );
212 			return false;
213 		}
214 		pSymbol++;
215 	}
216 	return true;
217 #endif // HAVE_DLOPEN
218 }
219 
220 #if !USE_WINDOWS
221 #define offsetof(T, M) \
222 	((int)(reinterpret_cast<char*>(&(((T*)1000)->M)) - reinterpret_cast<char*>(1000)))
223 #endif
224 
225 static SymbolDesc_t g_dSymbolsUDF[] =
226 {
227 	{ offsetof(PluginUDF_c, m_fnInit),		"init",		false },
228 	{ offsetof(PluginUDF_c, m_fnFunc),		"",			true },
229 	{ offsetof(PluginUDF_c, m_fnDeinit),	"deinit",	false },
230 	{ -1, 0, 0 }
231 };
232 
233 
234 static SymbolDesc_t g_dSymbolsRanker[] =
235 {
236 	{ offsetof(PluginRanker_c, m_fnInit),		"init",		false },
237 	{ offsetof(PluginRanker_c, m_fnUpdate),		"update",	false },
238 	{ offsetof(PluginRanker_c, m_fnFinalize),	"finalize",	true },
239 	{ offsetof(PluginRanker_c, m_fnDeinit),		"deinit",	false },
240 	{ -1, 0, 0 }
241 };
242 
243 
244 static SymbolDesc_t g_dSymbolsTokenFilter[] =
245 {
246 	{ offsetof(PluginTokenFilter_c, m_fnInit),			"init",				false },
247 	{ offsetof(PluginTokenFilter_c, m_fnBeginDocument),	"begin_document",	false },
248 	{ offsetof(PluginTokenFilter_c, m_fnBeginField),	"begin_field",		false },
249 	{ offsetof(PluginTokenFilter_c, m_fnPushToken),		"push_token",		true },
250 	{ offsetof(PluginTokenFilter_c, m_fnGetExtraToken),	"get_extra_token",	false },
251 	{ offsetof(PluginTokenFilter_c, m_fnEndField),		"end_field",		false },
252 	{ offsetof(PluginTokenFilter_c, m_fnDeinit),		"deinit",			false },
253 	{ -1, 0, 0 }
254 };
255 
256 
257 static SymbolDesc_t g_dSymbolsQueryTokenFilter[] =
258 {
259 	{ offsetof(PluginQueryTokenFilter_c, m_fnInit),			"init",			false },
260 	{ offsetof(PluginQueryTokenFilter_c, m_fnPreMorph),		"pre_morph",	false },
261 	{ offsetof(PluginQueryTokenFilter_c, m_fnPostMorph),	"post_morph",	false },
262 	{ offsetof(PluginQueryTokenFilter_c, m_fnDeinit),		"deinit",		false },
263 	{ -1, 0, 0 }
264 };
265 
266 
PluginCreate(const char * szLib,const char * szName,PluginType_e eType,PluginDesc_c * pPlugin,const SymbolDesc_t * pSymbols,CSphString & sError)267 static bool PluginCreate ( const char * szLib, const char * szName,
268 	PluginType_e eType, PluginDesc_c * pPlugin, const SymbolDesc_t * pSymbols, CSphString & sError )
269 {
270 #if !HAVE_DLOPEN
271 	sError = "no dlopen(), no plugins";
272 	delete pPlugin;
273 	return false;
274 #else
275 	if ( !g_bPluginsEnabled )
276 	{
277 		sError = "plugin support disabled (requires a valid plugin_dir)";
278 		delete pPlugin;
279 		return false;
280 	}
281 
282 	if ( g_bPluginsLocked )
283 	{
284 		sError = "CREATE is disabled (fully dynamic plugins require workers=threads)";
285 		delete pPlugin;
286 		return false;
287 	}
288 
289 	// validate library name
290 	for ( const char * p = szLib; *p; p++ )
291 		if ( *p=='/' || *p=='\\' )
292 		{
293 			sError = "restricted character (path delimiter) in a library file name";
294 			delete pPlugin;
295 			return false;
296 		}
297 
298 	CSphString sLib = szLib;
299 	sLib.ToLower();
300 
301 	// from here, we need a lock (we intend to update the plugin hash)
302 	CSphScopedLock<CSphStaticMutex> tLock ( g_tPluginMutex );
303 
304 	// validate function name
305 	PluginKey_t k ( eType, szName );
306 	if ( g_hPlugins(k) )
307 	{
308 		sError.SetSprintf ( "plugin '%s' already exists", k.m_sName.cstr() );
309 		delete pPlugin;
310 		return false;
311 	}
312 
313 	// lookup or load library
314 	CSphString sLibfile;
315 	sLibfile.SetSprintf ( "%s/%s", g_sPluginDir.cstr(), sLib.cstr() );
316 
317 	bool bJustLoaded = false;
318 	void * pHandle = NULL;
319 	pPlugin->m_pLib = g_hPluginLibs ( sLib.cstr() );
320 	if ( !pPlugin->m_pLib )
321 	{
322 		bJustLoaded = true;
323 		pHandle = dlopen ( sLibfile.cstr(), RTLD_LAZY | RTLD_LOCAL );
324 		if ( !pHandle )
325 		{
326 			const char * sDlerror = dlerror();
327 			sError.SetSprintf ( "dlopen() failed: %s", sDlerror ? sDlerror : "(null)" );
328 			delete pPlugin;
329 			return false;
330 		}
331 		sphLogDebug ( "dlopen(%s)=%p", sLibfile.cstr(), pHandle );
332 
333 	} else
334 		pHandle = pPlugin->m_pLib->m_pHandle;
335 
336 	assert ( pHandle );
337 
338 	if ( !PluginLoadSymbols ( pPlugin, pSymbols, pHandle, k.m_sName.cstr(), sError ) )
339 	{
340 		if ( bJustLoaded )
341 			dlclose ( pHandle );
342 
343 		sError.SetSprintf ( "%s in %s", sError.cstr(), sLib.cstr() );
344 		delete pPlugin;
345 		return false;
346 	}
347 
348 	// add library
349 	if ( bJustLoaded )
350 	{
351 		CSphString sBasename = sLib.cstr();
352 		const char * pDot = strchr ( sBasename.cstr(), '.' );
353 		if ( pDot )
354 			sBasename = sBasename.SubString ( 0, pDot-sBasename.cstr() );
355 
356 		CSphString sTmp;
357 		PluginVer_fn fnVer = (PluginVer_fn) dlsym ( pHandle, sTmp.SetSprintf ( "%s_ver", sBasename.cstr() ).cstr() );
358 		if ( !fnVer )
359 		{
360 			sError.SetSprintf ( "symbol '%s_ver' not found in '%s': update your UDF implementation", sBasename.cstr(), sLib.cstr() );
361 			dlclose ( pHandle );
362 			delete pPlugin;
363 			return false;
364 		}
365 
366 		if ( fnVer() < SPH_UDF_VERSION )
367 		{
368 			sError.SetSprintf ( "library '%s' was compiled using an older version of sphinxudf.h; it needs to be recompiled", sLib.cstr() );
369 			dlclose ( pHandle );
370 			delete pPlugin;
371 			return false;
372 		}
373 
374 		PluginLib_t tLib;
375 		memset ( tLib.m_dCount, 0, sizeof(tLib.m_dCount) );
376 		tLib.m_dCount[eType] = 1;
377 		tLib.m_pHandle = pHandle;
378 		tLib.m_fnReinit = (PluginReinit_fn) dlsym ( pHandle, sTmp.SetSprintf ( "%s_reinit", sBasename.cstr() ).cstr() );
379 		Verify ( g_hPluginLibs.Add ( tLib, sLib.cstr() ) );
380 		pPlugin->m_pLib = g_hPluginLibs ( sLib.cstr() );
381 	} else
382 		pPlugin->m_pLib->m_dCount[eType]++;
383 
384 	pPlugin->m_pLibName = g_hPluginLibs.GetKeyPtr ( sLib );
385 	assert ( pPlugin->m_pLib );
386 
387 	// add function
388 	Verify ( g_hPlugins.Add ( pPlugin, k ) );
389 	return true;
390 #endif // HAVE_DLOPEN
391 }
392 
393 
sphPluginCreate(const char * sLib,PluginType_e eType,const char * sName,ESphAttr eUDFRetType,CSphString & sError)394 bool sphPluginCreate ( const char * sLib, PluginType_e eType, const char * sName, ESphAttr eUDFRetType, CSphString & sError )
395 {
396 	// FIXME? preregister known rankers instead?
397 	if ( eType==PLUGIN_RANKER )
398 	{
399 		for ( int i=0; i<SPH_RANK_TOTAL; i++ )
400 		{
401 			const char * r = sphGetRankerName ( ESphRankMode(i) );
402 			if ( r && strcasecmp ( sName, r )==0 )
403 			{
404 				sError.SetSprintf ( "%s is a reserved ranker name", r );
405 				return false;
406 			}
407 		}
408 	}
409 
410 	PluginDesc_c * pDesc = NULL;
411 	const SymbolDesc_t * pSym = NULL;
412 	switch ( eType )
413 	{
414 		case PLUGIN_RANKER:					pDesc = new PluginRanker_c(); pSym = g_dSymbolsRanker; break;
415 		case PLUGIN_INDEX_TOKEN_FILTER:		pDesc = new PluginTokenFilter_c(); pSym = g_dSymbolsTokenFilter; break;
416 		case PLUGIN_QUERY_TOKEN_FILTER:		pDesc = new PluginQueryTokenFilter_c(); pSym = g_dSymbolsQueryTokenFilter; break;
417 		case PLUGIN_FUNCTION:				pDesc = new PluginUDF_c ( eUDFRetType ); pSym = g_dSymbolsUDF; break;
418 		default:
419 			sError.SetSprintf ( "INTERNAL ERROR: unknown plugin type %d in CreatePlugin()", (int)eType );
420 			return false;
421 	}
422 	return PluginCreate ( sLib, sName, eType, pDesc, pSym, sError );
423 }
424 
425 
sphPluginDrop(PluginType_e eType,const char * sName,CSphString & sError)426 bool sphPluginDrop ( PluginType_e eType, const char * sName, CSphString & sError )
427 {
428 #if !HAVE_DLOPEN
429 	sError = "no dlopen(), no plugins";
430 	return false;
431 #else
432 	if ( g_bPluginsLocked )
433 	{
434 		sError = "DROP is disabled (fully dynamic plugins require workers=threads)";
435 		return false;
436 	}
437 
438 	g_tPluginMutex.Lock();
439 
440 	PluginKey_t tKey ( eType, sName );
441 	PluginDesc_c ** ppPlugin = g_hPlugins(tKey);
442 	if ( !ppPlugin || !*ppPlugin || (**ppPlugin).m_bToDrop ) // handle concurrent drop in progress as "not exists"
443 	{
444 		sError.SetSprintf ( "plugin '%s' does not exist", sName );
445 		g_tPluginMutex.Unlock();
446 		return false;
447 	}
448 
449 	static const int UDF_DROP_TIMEOUT_SEC = 30; // in seconds
450 	int64_t tmEnd = sphMicroTimer() + UDF_DROP_TIMEOUT_SEC*1000000;
451 
452 	// mark for deletion, to prevent new users
453 	PluginDesc_c * pPlugin = *ppPlugin;
454 	pPlugin->m_bToDrop = true;
455 	if ( pPlugin->m_iUserCount )
456 		for ( ;; )
457 		{
458 			// release lock and wait
459 			// so that concurrent users could complete and release the plugin
460 			g_tPluginMutex.Unlock();
461 			sphSleepMsec ( 50 );
462 
463 			// re-acquire lock
464 			g_tPluginMutex.Lock();
465 
466 			// everyone out? proceed with dropping
467 			assert ( pPlugin->m_iUserCount>=0 );
468 			if ( pPlugin->m_iUserCount<=0 )
469 				break;
470 
471 			// timed out? clear deletion flag, and bail
472 			if ( sphMicroTimer() > tmEnd )
473 			{
474 				pPlugin->m_bToDrop = false;
475 				g_tPluginMutex.Unlock();
476 
477 				sError.SetSprintf ( "DROP timed out in (still got %d users after waiting for %d seconds); please retry",
478 					pPlugin->m_iUserCount, UDF_DROP_TIMEOUT_SEC );
479 				return false;
480 			}
481 		}
482 
483 	PluginLib_t * pLib = pPlugin->m_pLib;
484 	const CSphString * pLibName = pPlugin->m_pLibName;
485 
486 	Verify ( g_hPlugins.Delete(tKey) );
487 	pLib->m_dCount[eType]--;
488 
489 	bool bCanDrop = true;
490 	for ( int i=0; i<PLUGIN_TOTAL && bCanDrop; i++ )
491 		if ( pLib->m_dCount[i]>0 )
492 			bCanDrop = false;
493 
494 	if ( bCanDrop )
495 	{
496 		// FIXME! running queries might be using this function/ranker
497 		int iRes = dlclose ( pLib->m_pHandle );
498 		sphLogDebug ( "dlclose(%s)=%d", pLibName->cstr(), iRes );
499 		Verify ( g_hPluginLibs.Delete ( *pLibName ) );
500 	}
501 
502 	g_tPluginMutex.Unlock();
503 	return true;
504 #endif // HAVE_DLOPEN
505 }
506 
507 
sphPluginAcquire(const char * szLib,PluginType_e eType,const char * szName,CSphString & sError)508 PluginDesc_c * sphPluginAcquire ( const char * szLib, PluginType_e eType, const char * szName, CSphString & sError )
509 {
510 	PluginDesc_c * pDesc = sphPluginGet ( eType, szName );
511 	if ( !pDesc )
512 	{
513 		if ( !sphPluginCreate ( szLib, eType, szName, SPH_ATTR_NONE, sError ) )
514 			return NULL;
515 		return sphPluginGet ( eType, szName );
516 	}
517 
518 	CSphString sLib ( szLib );
519 	sLib.ToLower();
520 	if ( *(pDesc->m_pLibName)==sLib )
521 		return pDesc;
522 
523 	sError.SetSprintf ( "unable to load plugin '%s' from '%s': it has already been loaded from library '%s'",
524 		szName, sLib.cstr(), pDesc->m_pLibName->cstr() );
525 	pDesc->Release();
526 	return NULL;
527 }
528 
529 
UdfReturnType(ESphAttr eType)530 static const char * UdfReturnType ( ESphAttr eType )
531 {
532 	switch ( eType )
533 	{
534 		case SPH_ATTR_INTEGER:		return "INT";
535 		case SPH_ATTR_FLOAT:		return "FLOAT";
536 		case SPH_ATTR_STRINGPTR:	return "STRING";
537 		case SPH_ATTR_BIGINT:		return "BIGINT";
538 		default:					assert ( 0 && "unknown UDF return type" ); return "???";
539 	}
540 }
541 
542 
sphPluginSaveState(CSphWriter & tWriter)543 void sphPluginSaveState ( CSphWriter & tWriter )
544 {
545 	CSphScopedLock<CSphStaticMutex> tLock ( g_tPluginMutex );
546 	g_hPlugins.IterateStart();
547 	while ( g_hPlugins.IterateNext() )
548 	{
549 		const PluginKey_t & k = g_hPlugins.IterateGetKey();
550 		const PluginDesc_c * v = g_hPlugins.IterateGet();
551 		if ( v->m_bToDrop )
552 			continue;
553 
554 		CSphString sBuf;
555 		if ( k.m_eType==PLUGIN_FUNCTION )
556 			sBuf.SetSprintf ( "CREATE FUNCTION %s RETURNS %s SONAME '%s';\n", k.m_sName.cstr(),
557 				UdfReturnType ( ((PluginUDF_c*)v)->m_eRetType ), v->m_pLibName->cstr() );
558 		else
559 			sBuf.SetSprintf ( "CREATE PLUGIN %s TYPE '%s' SONAME '%s';\n",
560 				k.m_sName.cstr(), g_dPluginTypes[k.m_eType], v->m_pLibName->cstr() );
561 
562 		tWriter.PutBytes ( sBuf.cstr(), sBuf.Length() );
563 	}
564 }
565 
566 
sphPluginReinit()567 void sphPluginReinit()
568 {
569 	CSphScopedLock<CSphStaticMutex> tLock ( g_tPluginMutex );
570 	g_hPluginLibs.IterateStart();
571 	while ( g_hPluginLibs.IterateNext() )
572 	{
573 		const PluginLib_t & tLib = g_hPluginLibs.IterateGet();
574 		if ( tLib.m_fnReinit )
575 			tLib.m_fnReinit();
576 	}
577 }
578 
579 
sphPluginGetType(const CSphString & s)580 PluginType_e sphPluginGetType ( const CSphString & s )
581 {
582 	if ( s=="ranker" )				return PLUGIN_RANKER;
583 	if ( s=="index_token_filter" )	return PLUGIN_INDEX_TOKEN_FILTER;
584 	if ( s=="query_token_filter" )	return PLUGIN_QUERY_TOKEN_FILTER;
585 	return PLUGIN_TOTAL;
586 }
587 
588 
sphPluginExists(PluginType_e eType,const char * sName)589 bool sphPluginExists ( PluginType_e eType, const char * sName )
590 {
591 	if ( !g_bPluginsEnabled )
592 		return false;
593 	CSphScopedLock<CSphStaticMutex> tLock ( g_tPluginMutex );
594 	PluginKey_t k ( eType, sName );
595 	PluginDesc_c ** pp = g_hPlugins(k);
596 	return pp && *pp && !(**pp).m_bToDrop;
597 }
598 
599 
sphPluginGet(PluginType_e eType,const char * sName)600 PluginDesc_c * sphPluginGet ( PluginType_e eType, const char * sName )
601 {
602 	if ( !g_bPluginsEnabled )
603 		return NULL;
604 
605 	CSphScopedLock<CSphStaticMutex> tLock ( g_tPluginMutex );
606 	PluginKey_t k ( eType, sName );
607 	PluginDesc_c ** pp = g_hPlugins(k);
608 	if ( !pp || !*pp || (**pp).m_bToDrop )
609 		return NULL; // either not found, or DROP in progress, can not use
610 	(**pp).m_iUserCount++; // protection against concurrent DROP, gets decremented in PluginDesc_c::Release()
611 	return *pp;
612 }
613 
614 
sphPluginList(CSphVector<PluginInfo_t> & dResult)615 void sphPluginList ( CSphVector<PluginInfo_t> & dResult )
616 {
617 	if ( !g_bPluginsEnabled )
618 		return;
619 	CSphScopedLock<CSphStaticMutex> tLock ( g_tPluginMutex );
620 	g_hPlugins.IterateStart();
621 	while ( g_hPlugins.IterateNext() )
622 	{
623 		const PluginKey_t & k = g_hPlugins.IterateGetKey();
624 		const PluginDesc_c *v = g_hPlugins.IterateGet();
625 
626 		PluginInfo_t & p = dResult.Add();
627 		p.m_eType = k.m_eType;
628 		p.m_sName = k.m_sName;
629 		p.m_sLib = v->m_pLibName->cstr();
630 		p.m_iUsers = v->m_iUserCount;
631 		if ( p.m_eType==PLUGIN_FUNCTION )
632 			p.m_sExtra = UdfReturnType ( ((PluginUDF_c*)v)->m_eRetType );
633 	}
634 }
635 
636 //
637 // $Id$
638 //
639