sphinxplugin.cpp 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759
  1. //
  2. // Copyright (c) 2017-2026, Manticore Software LTD (https://manticoresearch.com)
  3. // Copyright (c) 2001-2016, Andrew Aksyonoff
  4. // Copyright (c) 2008-2016, Sphinx Technologies Inc
  5. // All rights reserved
  6. //
  7. // This program is free software; you can redistribute it and/or modify
  8. // it under the terms of the GNU General Public License. You should have
  9. // received a copy of the GPL license along with this program; if you
  10. // did not, you can find it at http://www.gnu.org/
  11. //
  12. #include "sphinxint.h"
  13. #include "sphinxutils.h"
  14. #include "sphinxplugin.h"
  15. // for HAVE_DLOPEN
  16. #include "libutils.h"
  17. //////////////////////////////////////////////////////////////////////////
  18. // TYPES
  19. //////////////////////////////////////////////////////////////////////////
  20. /// loaded plugin library
  21. class PluginLib_c final : public ISphRefcountedMT
  22. {
  23. protected:
  24. CSphString m_sName;
  25. void * m_pHandle; ///< handle from dlopen()
  26. public:
  27. int m_iHashedPlugins; ///< how many active g_hPlugins entries reference this handle
  28. bool m_bDlGlobal = false;
  29. PluginLib_c ( void * pHandle, const char * sName, bool bDlGlobal );
  30. const CSphString & GetName() const { return m_sName; }
  31. void * GetHandle() const { return m_pHandle; }
  32. protected:
  33. ~PluginLib_c() final;
  34. };
  35. PluginLibRefPtr_c PluginDesc_c::GetLib() const
  36. {
  37. return m_pLib;
  38. }
  39. /// plugin key
  40. struct PluginKey_t
  41. {
  42. PluginType_e m_eType { PLUGIN_FUNCTION };
  43. CSphString m_sName;
  44. PluginKey_t() = default;
  45. PluginKey_t ( PluginType_e eType, const char * sName )
  46. : m_eType ( eType )
  47. , m_sName ( sName )
  48. {
  49. m_sName.ToLower();
  50. }
  51. static int Hash ( const PluginKey_t & v )
  52. {
  53. return sphCRC32 ( v.m_sName.cstr(), v.m_sName.Length(),
  54. sphCRC32 ( &v.m_eType, sizeof(v.m_eType) ) );
  55. }
  56. bool operator == ( const PluginKey_t & rhs )
  57. {
  58. return m_eType==rhs.m_eType && m_sName==rhs.m_sName;
  59. }
  60. };
  61. //////////////////////////////////////////////////////////////////////////
  62. // GLOBALS
  63. //////////////////////////////////////////////////////////////////////////
  64. const char * g_dPluginTypes[PLUGIN_TOTAL] = { "udf", "ranker", "index_token_filter", "query_token_filter" };
  65. //////////////////////////////////////////////////////////////////////////
  66. static bool g_bPluginsEnabled = true; ///< is there any plugin support at all?
  67. static CSphString g_sPluginDir = HARDCODED_PLUGIN_DIR;
  68. static CSphMutex g_tPluginMutex; ///< common plugin mutex (access to lib, func and ranker hashes)
  69. static SmallStringHash_T<PluginLibRefPtr_c> g_hPluginLibs GUARDED_BY ( g_tPluginMutex ); ///< key is the filename (no path)
  70. static CSphOrderedHash<PluginDescRefPtr_c, PluginKey_t, PluginKey_t, 256> g_hPlugins GUARDED_BY ( g_tPluginMutex );
  71. //////////////////////////////////////////////////////////////////////////
  72. // PLUGIN MANAGER
  73. //////////////////////////////////////////////////////////////////////////
  74. PluginLib_c::PluginLib_c ( void * pHandle, const char * sName, bool bDlGlobal )
  75. {
  76. assert ( pHandle );
  77. m_pHandle = pHandle;
  78. m_iHashedPlugins = 0;
  79. m_sName = sName;
  80. m_sName.ToLower();
  81. m_bDlGlobal = bDlGlobal;
  82. }
  83. PluginLib_c::~PluginLib_c()
  84. {
  85. #if HAVE_DLOPEN
  86. int iRes = dlclose ( m_pHandle );
  87. sphLogDebug ( "dlclose(%s)=%d", m_sName.cstr(), iRes );
  88. #endif
  89. }
  90. PluginDesc_c::PluginDesc_c ( PluginLibRefPtr_c pLib )
  91. {
  92. assert ( pLib );
  93. m_pLib = std::move (pLib);
  94. }
  95. PluginDesc_c::~PluginDesc_c() {}
  96. const CSphString & PluginDesc_c::GetLibName() const
  97. {
  98. assert ( m_pLib );
  99. return m_pLib->GetName();
  100. }
  101. //////////////////////////////////////////////////////////////////////////
  102. static bool SetPluginDir ( const char * sDir, CSphString & sPlugin )
  103. {
  104. bool bEmpty = ( !sDir || !*sDir );
  105. if ( !bEmpty )
  106. {
  107. if ( sphDirExists ( sDir, nullptr ) )
  108. {
  109. sPlugin = sDir;
  110. return true;
  111. }
  112. }
  113. if ( sphDirExists ( HARDCODED_PLUGIN_DIR, nullptr ) )
  114. {
  115. sPlugin = HARDCODED_PLUGIN_DIR;
  116. return true;
  117. } else
  118. {
  119. #if _WIN32
  120. CSphString sWinInstall = GetWinInstallDir();
  121. if ( !sWinInstall.IsEmpty() )
  122. {
  123. CSphString sWinInstallPlugin;
  124. sWinInstallPlugin.SetSprintf ( "%s/%s", sWinInstall.cstr(), HARDCODED_PLUGIN_DIR );
  125. if ( sphDirExists ( sWinInstallPlugin, nullptr ) )
  126. {
  127. sPlugin = sWinInstallPlugin;
  128. return true;
  129. }
  130. }
  131. #endif
  132. }
  133. return false;
  134. }
  135. void sphPluginInit ( const char * sDir )
  136. {
  137. g_bPluginsEnabled = SetPluginDir ( sDir, g_sPluginDir );
  138. }
  139. const CSphString & PluginGetDir()
  140. {
  141. return g_sPluginDir;
  142. }
  143. bool sphPluginParseSpec ( const CSphString & sParams, StrVec_t & dParams, CSphString & sError )
  144. {
  145. dParams.Resize ( 0 );
  146. sphSplit ( dParams, sParams.cstr(), ":" );
  147. switch ( dParams.GetLength() )
  148. {
  149. case 0:
  150. return true;
  151. case 1:
  152. sError = "filter name required in spec string; example: \"plugins.so:myfilter\"";
  153. return false;
  154. case 2:
  155. dParams.Add ( "" );
  156. return true;
  157. case 3:
  158. return true;
  159. }
  160. sError = "too many parts in spec string; must be in \"plugins.so:myfilter:options\" format";
  161. return false;
  162. }
  163. struct SymbolDesc_t
  164. {
  165. int m_iOffsetOf; ///< pointer member location in the descriptor structure
  166. const char * m_sPostfix; ///< symbol name postfix
  167. bool m_bRequired; ///< whether this symbol must be present
  168. };
  169. #if HAVE_DLOPEN
  170. static bool PluginLoadSymbols ( void * pDesc, const SymbolDesc_t * pSymbol, void * pHandle, const char * sName, CSphString & sError )
  171. {
  172. // sError = "no dlopen(), no plugins";
  173. // return false;
  174. CSphString s;
  175. while ( pSymbol->m_iOffsetOf>=0 )
  176. {
  177. s.SetSprintf ( pSymbol->m_sPostfix[0] ? "%s_%s" : "%s%s", sName, pSymbol->m_sPostfix );
  178. auto ** ppFunc = (void**)((BYTE*)pDesc + pSymbol->m_iOffsetOf);
  179. *ppFunc = dlsym ( pHandle, s.cstr() );
  180. if ( !*ppFunc && pSymbol->m_bRequired )
  181. {
  182. sError.SetSprintf ( "symbol %s() not found", s.cstr() );
  183. return false;
  184. }
  185. pSymbol++;
  186. }
  187. return true;
  188. }
  189. #endif // HAVE_DLOPEN
  190. #if !_WIN32
  191. #ifndef offsetof
  192. #define offsetof(T, M) \
  193. (reinterpret_cast<char*>(&(((T*)1000)->M)) - reinterpret_cast<char*>(1000))
  194. #endif
  195. #endif
  196. #if HAVE_DLOPEN
  197. #ifdef __GNUC__
  198. #pragma GCC diagnostic push
  199. #pragma GCC diagnostic ignored "-Winvalid-offsetof"
  200. #endif
  201. static SymbolDesc_t g_dSymbolsUDF[] =
  202. {
  203. { static_cast<int>( offsetof(PluginUDF_c, m_fnInit)), "init", false },
  204. { static_cast<int>( offsetof(PluginUDF_c, m_fnFunc)), "", true },
  205. { static_cast<int>( offsetof(PluginUDF_c, m_fnDeinit)), "deinit", false },
  206. { -1, nullptr, false }
  207. };
  208. static SymbolDesc_t g_dSymbolsRanker[] =
  209. {
  210. { static_cast<int>( offsetof(PluginRanker_c, m_fnInit)), "init", false },
  211. { static_cast<int>( offsetof(PluginRanker_c, m_fnUpdate)), "update", false },
  212. { static_cast<int>( offsetof(PluginRanker_c, m_fnFinalize)), "finalize", true },
  213. { static_cast<int>( offsetof(PluginRanker_c, m_fnDeinit)), "deinit", false },
  214. { -1, nullptr, false }
  215. };
  216. static SymbolDesc_t g_dSymbolsTokenFilter[] =
  217. {
  218. { static_cast<int>( offsetof(PluginTokenFilter_c, m_fnInit)), "init", false },
  219. { static_cast<int>( offsetof(PluginTokenFilter_c, m_fnBeginDocument)), "begin_document", false },
  220. { static_cast<int>( offsetof(PluginTokenFilter_c, m_fnBeginField)), "begin_field", false },
  221. { static_cast<int>( offsetof(PluginTokenFilter_c, m_fnPushToken)), "push_token", true },
  222. { static_cast<int>( offsetof(PluginTokenFilter_c, m_fnGetExtraToken)), "get_extra_token", false },
  223. { static_cast<int>( offsetof(PluginTokenFilter_c, m_fnEndField)), "end_field", false },
  224. { static_cast<int>( offsetof(PluginTokenFilter_c, m_fnDeinit)), "deinit", false },
  225. { static_cast<int>( offsetof(PluginTokenFilter_c, m_fnTokenIsBlended)), "is_blended", false },
  226. { static_cast<int>( offsetof(PluginTokenFilter_c, m_fnTokenIsBlendedPart)), "is_blended_part", false },
  227. { -1, nullptr, false }
  228. };
  229. static SymbolDesc_t g_dSymbolsQueryTokenFilter[] =
  230. {
  231. { static_cast<int>( offsetof(PluginQueryTokenFilter_c, m_fnInit)), "init", false },
  232. { static_cast<int>( offsetof(PluginQueryTokenFilter_c, m_fnPreMorph)), "pre_morph", false },
  233. { static_cast<int>( offsetof(PluginQueryTokenFilter_c, m_fnPostMorph)), "post_morph", false },
  234. { static_cast<int>( offsetof(PluginQueryTokenFilter_c, m_fnPushToken)), "push_token", false },
  235. { static_cast<int>( offsetof(PluginQueryTokenFilter_c, m_fnDeinit)), "deinit", false },
  236. { -1, nullptr, false }
  237. };
  238. void PluginLog ( const char * szMsg, int iLen )
  239. {
  240. if ( iLen<0 )
  241. sphWarning ( "PLUGIN: %s", szMsg );
  242. else
  243. sphWarning ( "PLUGIN: %.*s", (int) iLen, szMsg );
  244. }
  245. static bool PluginOnLoadLibrary ( const PluginLibRefPtr_c& pLib, CSphString & sError ) REQUIRES ( g_tPluginMutex )
  246. {
  247. // library already loaded - no need to call plugin_load function
  248. if ( g_hPluginLibs ( pLib->GetName() ) )
  249. return true;
  250. auto fnPluginLoad = (PluginLoad_fn) dlsym ( pLib->GetHandle(), "plugin_load" );
  251. if ( fnPluginLoad )
  252. {
  253. char sErrBuf [ SPH_UDF_ERROR_LEN ] = { 0 };
  254. if ( fnPluginLoad ( sErrBuf )!=0 )
  255. {
  256. sError = sErrBuf;
  257. return false;
  258. }
  259. }
  260. return true;
  261. }
  262. static bool PluginOnUnloadLibrary ( const PluginLibRefPtr_c& pLib, CSphString & sError )
  263. {
  264. auto fnPluginUnload = (PluginLoad_fn) dlsym ( pLib->GetHandle(), "plugin_unload" );
  265. if ( fnPluginUnload )
  266. {
  267. char sErrBuf [ SPH_UDF_ERROR_LEN ] = { 0 };
  268. if ( fnPluginUnload ( sErrBuf )!=0 )
  269. {
  270. sError = sErrBuf;
  271. return false;
  272. }
  273. }
  274. return true;
  275. }
  276. static PluginLibRefPtr_c LoadPluginLibrary ( const char * sLibName, CSphString & sError, bool bDlGlobal, bool bLinuxReload )
  277. {
  278. CSphString sTmpfile;
  279. CSphString sLibfile;
  280. sLibfile.SetSprintf ( "%s/%s", g_sPluginDir.cstr(), sLibName );
  281. // dlopen caches the old file content, even if file was updated
  282. // let's reload library from the temporary file to invalidate the cache
  283. if ( bLinuxReload )
  284. {
  285. sTmpfile.SetSprintf ( "%s/%s.%u", g_sPluginDir.cstr(), sLibName, sphRand() );
  286. if ( sph::rename ( sLibfile.cstr(), sTmpfile.cstr() ) )
  287. {
  288. sError.SetSprintf ( "failed to rename file (src=%s, dst=%s, errno=%d, error=%s)", sLibfile.cstr(), sTmpfile.cstr(), errno, strerrorm(errno) );
  289. return nullptr;
  290. }
  291. }
  292. int iFlags = ( bDlGlobal ? ( RTLD_LAZY | RTLD_GLOBAL ) : ( RTLD_LAZY | RTLD_LOCAL ) );
  293. void * pHandle = dlopen ( bLinuxReload ? sTmpfile.cstr() : sLibfile.cstr(), iFlags );
  294. if ( !pHandle )
  295. {
  296. const char * sDlerror = dlerror();
  297. sError.SetSprintf ( "dlopen() failed: %s", sDlerror ? sDlerror : "(null)" );
  298. return nullptr;
  299. }
  300. sphLogDebug ( "dlopen(%s)=%p", bLinuxReload ? sTmpfile.cstr() : sLibfile.cstr(), pHandle );
  301. // rename file back to the original name
  302. if ( bLinuxReload )
  303. {
  304. if ( sph::rename ( sTmpfile.cstr(), sLibfile.cstr() ) )
  305. {
  306. sError.SetSprintf ( "failed to rename file (src=%s, dst=%s, errno=%d, error=%s)", sTmpfile.cstr(), sLibfile.cstr(), errno, strerrorm(errno) );
  307. dlclose ( pHandle );
  308. return nullptr;
  309. }
  310. }
  311. CSphString sBasename = sLibName;
  312. const char * pDot = strchr ( sBasename.cstr(), '.' );
  313. if ( pDot )
  314. sBasename = sBasename.SubString ( 0, pDot-sBasename.cstr() );
  315. CSphString sTmp;
  316. auto fnVer = (PluginVer_fn) dlsym ( pHandle, sTmp.SetSprintf ( "%s_ver", sBasename.cstr() ).cstr() );
  317. if ( !fnVer )
  318. {
  319. sError.SetSprintf ( "symbol '%s_ver' not found in '%s': update your UDF implementation", sBasename.cstr(), sLibName );
  320. dlclose ( pHandle );
  321. return nullptr;
  322. }
  323. if ( fnVer() < SPH_UDF_VERSION )
  324. {
  325. sError.SetSprintf ( "library '%s' was compiled using an older version of sphinxudf.h; it needs to be recompiled", sLibName );
  326. dlclose ( pHandle );
  327. return nullptr;
  328. }
  329. auto fnLogCb = (PluginLogCb_fn) dlsym ( pHandle, sTmp.SetSprintf ( "%s_setlogcb", sBasename.cstr ()).cstr ());
  330. if ( fnLogCb ) {
  331. fnLogCb(PluginLog);
  332. }
  333. return PluginLibRefPtr_c { new PluginLib_c ( pHandle, sLibName, bDlGlobal ) };
  334. }
  335. #endif
  336. bool sphPluginCreate ( const char * szLib, PluginType_e eType, const char * sName, ESphAttr eUDFRetType, CSphString & sError )
  337. {
  338. return sphPluginCreate ( szLib, eType, sName, eUDFRetType, false, sError );
  339. }
  340. bool sphPluginCreate ( const char * szLib, PluginType_e eType, const char * sName, ESphAttr eUDFRetType, bool bDlGlobal, CSphString & sError )
  341. {
  342. #if !HAVE_DLOPEN
  343. sError = "no dlopen(), no plugins";
  344. return false;
  345. #else
  346. if ( !g_bPluginsEnabled )
  347. {
  348. sError = "plugin support disabled (requires a valid plugin_dir)";
  349. return false;
  350. }
  351. // validate library name
  352. for ( const char * p = szLib; *p; p++ )
  353. if ( *p=='/' || *p=='\\' )
  354. {
  355. sError = "restricted character (path delimiter) in a library file name";
  356. return false;
  357. }
  358. CSphString sLib = szLib;
  359. sLib.ToLower();
  360. // FIXME? preregister known rankers instead?
  361. if ( eType==PLUGIN_RANKER )
  362. {
  363. for ( int i=0; i<SPH_RANK_TOTAL; i++ )
  364. {
  365. const char * r = sphGetRankerName ( ESphRankMode(i) );
  366. if ( r && strcasecmp ( sName, r )==0 )
  367. {
  368. sError.SetSprintf ( "%s is a reserved ranker name", r );
  369. return false;
  370. }
  371. }
  372. }
  373. // from here, we need a lock (we intend to update the plugin hash)
  374. ScopedMutex_t tLock ( g_tPluginMutex );
  375. // validate function name
  376. PluginKey_t k ( eType, sName );
  377. if ( g_hPlugins(k) )
  378. {
  379. sError.SetSprintf ( "plugin '%s' already exists", k.m_sName.cstr() );
  380. return false;
  381. }
  382. // lookup or load library
  383. PluginLibRefPtr_c pLib;
  384. if ( g_hPluginLibs ( sLib ) )
  385. pLib = g_hPluginLibs [ sLib ];
  386. else
  387. pLib = LoadPluginLibrary ( sLib.cstr(), sError, bDlGlobal, false );
  388. if ( !pLib )
  389. return false;
  390. assert ( pLib->GetHandle() );
  391. if ( !PluginOnLoadLibrary ( pLib, sError ) )
  392. return false;
  393. PluginDescRefPtr_c pPlugin;
  394. const SymbolDesc_t * pSym = nullptr;
  395. switch ( eType )
  396. {
  397. case PLUGIN_RANKER: pPlugin = new PluginRanker_c ( pLib ); pSym = g_dSymbolsRanker; break;
  398. case PLUGIN_INDEX_TOKEN_FILTER: pPlugin = new PluginTokenFilter_c ( pLib ); pSym = g_dSymbolsTokenFilter; break;
  399. case PLUGIN_QUERY_TOKEN_FILTER: pPlugin = new PluginQueryTokenFilter_c ( pLib ); pSym = g_dSymbolsQueryTokenFilter; break;
  400. case PLUGIN_FUNCTION: pPlugin = new PluginUDF_c ( pLib, eUDFRetType ); pSym = g_dSymbolsUDF; break;
  401. default:
  402. sError.SetSprintf ( "INTERNAL ERROR: unknown plugin type %d in CreatePlugin()", (int)eType );
  403. return false;
  404. }
  405. if ( !PluginLoadSymbols ( pPlugin, pSym, pLib->GetHandle(), k.m_sName.cstr(), sError ) )
  406. {
  407. sError.SetSprintf ( "%s in %s", sError.cstr(), sLib.cstr() );
  408. return false;
  409. }
  410. // add library if needed
  411. if ( !g_hPluginLibs ( sLib ) )
  412. Verify ( g_hPluginLibs.Add ( pLib, pLib->GetName() ) );
  413. // add function
  414. Verify ( g_hPlugins.Add ( pPlugin, k ) );
  415. ++pPlugin->GetLib()->m_iHashedPlugins;
  416. return true;
  417. #endif // HAVE_DLOPEN
  418. }
  419. bool sphPluginDrop ( PluginType_e eType, const char * sName, CSphString & sError )
  420. {
  421. #if !HAVE_DLOPEN
  422. sError = "no dlopen(), no plugins";
  423. return false;
  424. #else
  425. ScopedMutex_t tLock ( g_tPluginMutex );
  426. PluginKey_t tKey ( eType, sName );
  427. PluginDescRefPtr_c* ppPlugin = g_hPlugins(tKey);
  428. if ( !ppPlugin || !*ppPlugin )
  429. {
  430. sError.SetSprintf ( "plugin '%s' does not exist", sName );
  431. return false;
  432. }
  433. PluginLibRefPtr_c pLib = ( *ppPlugin )->GetLib();
  434. Verify ( g_hPlugins.Delete(tKey) );
  435. bool bUnloaded = true;
  436. if ( --pLib->m_iHashedPlugins==0 )
  437. {
  438. bUnloaded = PluginOnUnloadLibrary ( pLib, sError );
  439. g_hPluginLibs.Delete ( pLib->GetName() );
  440. }
  441. return bUnloaded;
  442. #endif // HAVE_DLOPEN
  443. }
  444. bool sphPluginReload ( const char * sName, CSphString & sError )
  445. {
  446. #if !HAVE_DLOPEN
  447. sError = "no dlopen(), no plugins";
  448. return false;
  449. #else
  450. // find all plugins from the given library
  451. ScopedMutex_t tLock ( g_tPluginMutex );
  452. CSphVector<PluginKey_t> dKeys;
  453. CSphVector<PluginDescRefPtr_c> dPlugins;
  454. bool bDlGlobal = false;
  455. for ( const auto& tPlugin : g_hPlugins )
  456. {
  457. PluginDescRefPtr_c v = tPlugin.second;
  458. if ( v->GetLibName()==sName )
  459. {
  460. dKeys.Add ( tPlugin.first );
  461. dPlugins.Add ( v );
  462. bDlGlobal = v->GetLib()->m_bDlGlobal;
  463. }
  464. }
  465. // no plugins loaded? oops
  466. if ( dPlugins.GetLength()==0 )
  467. {
  468. sError.SetSprintf ( "no active plugins loaded from %s", sName );
  469. return false;
  470. }
  471. // load new library and check every plugin
  472. PluginLibRefPtr_c pNewLib = LoadPluginLibrary ( sName, sError, bDlGlobal,
  473. #if !_WIN32
  474. true
  475. #else
  476. false
  477. #endif
  478. );
  479. if ( !pNewLib )
  480. return false;
  481. // load all plugins
  482. CSphVector<PluginDescRefPtr_c> dNewPlugins;
  483. ARRAY_FOREACH ( i, dPlugins )
  484. {
  485. PluginDescRefPtr_c pDesc;
  486. const SymbolDesc_t * pSym = nullptr;
  487. switch ( dKeys[i].m_eType )
  488. {
  489. case PLUGIN_RANKER: pDesc = new PluginRanker_c ( pNewLib ); pSym = g_dSymbolsRanker; break;
  490. case PLUGIN_INDEX_TOKEN_FILTER: pDesc = new PluginTokenFilter_c ( pNewLib ); pSym = g_dSymbolsTokenFilter; break;
  491. case PLUGIN_QUERY_TOKEN_FILTER: pDesc = new PluginQueryTokenFilter_c ( pNewLib ); pSym = g_dSymbolsQueryTokenFilter; break;
  492. case PLUGIN_FUNCTION: pDesc = new PluginUDF_c ( pNewLib, dPlugins[i]->GetUdfRetType() ); pSym = g_dSymbolsUDF; break;
  493. default:
  494. sphDie ( "INTERNAL ERROR: unknown plugin type %d in sphPluginReload()", (int)dKeys[i].m_eType );
  495. return false;
  496. }
  497. if ( !PluginLoadSymbols ( pDesc, pSym, pNewLib->GetHandle(), dKeys[i].m_sName.cstr(), sError ) )
  498. break;
  499. dNewPlugins.Add ( pDesc );
  500. }
  501. // if there was a problem loading any of the plugins, time to fail
  502. if ( dPlugins.GetLength()!=dNewPlugins.GetLength() )
  503. {
  504. sError.SetSprintf ( "failed to import plugin %s: %s", dKeys [ dNewPlugins.GetLength() ].m_sName.cstr(), sError.cstr() );
  505. return false;
  506. }
  507. // unregister and release the old references
  508. PluginLibRefPtr_c pOldLib = dPlugins[0]->GetLib();
  509. ARRAY_FOREACH ( i, dPlugins )
  510. {
  511. assert ( dPlugins[i]->GetLib()==pOldLib );
  512. Verify ( g_hPlugins.Delete ( dKeys[i] ) );
  513. }
  514. assert ( pOldLib->m_iHashedPlugins==dPlugins.GetLength() );
  515. pOldLib->m_iHashedPlugins = 0;
  516. PluginOnUnloadLibrary ( pOldLib, sError );
  517. Verify ( g_hPluginLibs.Delete ( pOldLib->GetName() ) );
  518. if ( !PluginOnLoadLibrary ( pNewLib, sError ) )
  519. return false;
  520. // register new references
  521. g_hPluginLibs.Add ( pNewLib, pNewLib->GetName() );
  522. ARRAY_FOREACH ( i, dNewPlugins )
  523. Verify ( g_hPlugins.Add ( dNewPlugins[i], dKeys[i] ) );
  524. assert ( pNewLib->m_iHashedPlugins==0 );
  525. pNewLib->m_iHashedPlugins = dNewPlugins.GetLength();
  526. sphLogDebug ( "reloaded %d plugins", dNewPlugins.GetLength() );
  527. return true;
  528. #endif // HAVE_DLOPEN
  529. }
  530. PluginDescRefPtr_c PluginAcquireDesc ( const char * szLib, PluginType_e eType, const char * szName, CSphString & sError )
  531. {
  532. PluginDescRefPtr_c pDesc = PluginGetDesc ( eType, szName );
  533. if ( !pDesc )
  534. {
  535. if ( !sphPluginCreate ( szLib, eType, szName, SPH_ATTR_NONE, false, sError ) )
  536. return nullptr;
  537. return PluginGetDesc ( eType, szName );
  538. }
  539. CSphString sLib ( szLib );
  540. sLib.ToLower();
  541. if ( pDesc->GetLibName()==sLib )
  542. return pDesc;
  543. sError.SetSprintf ( "unable to load plugin '%s' from '%s': it has already been loaded from library '%s'",
  544. szName, sLib.cstr(), pDesc->GetLibName().cstr() );
  545. return nullptr;
  546. }
  547. static const char * UdfReturnType ( ESphAttr eType )
  548. {
  549. switch ( eType )
  550. {
  551. case SPH_ATTR_INTEGER: return "INT";
  552. case SPH_ATTR_FLOAT: return "FLOAT";
  553. case SPH_ATTR_STRINGPTR: return "STRING";
  554. case SPH_ATTR_BIGINT: return "BIGINT";
  555. default: assert ( 0 && "unknown UDF return type" ); return "???";
  556. }
  557. }
  558. void sphPluginSaveState ( CSphWriter & tWriter )
  559. {
  560. ScopedMutex_t tLock ( g_tPluginMutex );
  561. for ( const auto& tPlugin : g_hPlugins )
  562. {
  563. const PluginKey_t & k = tPlugin.first;
  564. const PluginDescRefPtr_c v = tPlugin.second;
  565. CSphString sBuf;
  566. if ( k.m_eType==PLUGIN_FUNCTION )
  567. sBuf.SetSprintf ( "CREATE FUNCTION %s RETURNS %s SONAME '%s';\n", k.m_sName.cstr(),
  568. UdfReturnType ( ((PluginUDF_c*)v.Ptr())->m_eRetType ), v->GetLibName().cstr() );
  569. else
  570. sBuf.SetSprintf ( "CREATE PLUGIN %s TYPE '%s' SONAME '%s';\n",
  571. k.m_sName.cstr(), g_dPluginTypes[k.m_eType], v->GetLibName().cstr() );
  572. tWriter.PutBytes ( sBuf.cstr(), sBuf.Length() );
  573. }
  574. }
  575. PluginType_e sphPluginGetType ( const CSphString & s )
  576. {
  577. if ( s=="ranker" ) return PLUGIN_RANKER;
  578. if ( s=="index_token_filter" ) return PLUGIN_INDEX_TOKEN_FILTER;
  579. if ( s=="query_token_filter" ) return PLUGIN_QUERY_TOKEN_FILTER;
  580. return PLUGIN_TOTAL;
  581. }
  582. bool sphPluginExists ( PluginType_e eType, const char * sName )
  583. {
  584. if ( !g_bPluginsEnabled )
  585. return false;
  586. ScopedMutex_t tLock ( g_tPluginMutex );
  587. PluginKey_t k ( eType, sName );
  588. PluginDescRefPtr_c* pp = g_hPlugins(k);
  589. return pp && *pp;
  590. }
  591. PluginDescRefPtr_c PluginGetDesc ( PluginType_e eType, const char * sName )
  592. {
  593. if ( !g_bPluginsEnabled )
  594. return nullptr;
  595. ScopedMutex_t tLock ( g_tPluginMutex );
  596. PluginKey_t k ( eType, sName );
  597. PluginDescRefPtr_c* pp = g_hPlugins(k);
  598. if ( !pp || !*pp )
  599. return nullptr;
  600. return *pp;
  601. }
  602. void sphPluginList ( CSphVector<PluginInfo_t> & dResult )
  603. {
  604. if ( !g_bPluginsEnabled )
  605. return;
  606. ScopedMutex_t tLock ( g_tPluginMutex );
  607. for ( const auto& tPlugin : g_hPlugins )
  608. {
  609. const PluginKey_t & k = tPlugin.first;
  610. const PluginDescRefPtr_c v = tPlugin.second;
  611. PluginInfo_t & p = dResult.Add();
  612. p.m_eType = k.m_eType;
  613. p.m_sName = k.m_sName;
  614. p.m_sLib = v->GetLibName().cstr();
  615. p.m_iUsers = v->GetRefcount() - 1; // except the one reference from the hash itself
  616. if ( p.m_eType==PLUGIN_FUNCTION )
  617. p.m_sExtra = UdfReturnType ( ((PluginUDF_c*)v.Ptr())->m_eRetType );
  618. }
  619. }
  620. PluginUDF_c::PluginUDF_c ( PluginLibRefPtr_c pLib, ESphAttr eRetType )
  621. : PluginDesc_c ( std::move ( pLib ) )
  622. , m_eRetType ( eRetType )
  623. {}
  624. PluginRanker_c::PluginRanker_c ( PluginLibRefPtr_c pLib )
  625. : PluginDesc_c ( std::move ( pLib ) )
  626. {}
  627. PluginTokenFilter_c::PluginTokenFilter_c ( PluginLibRefPtr_c pLib )
  628. : PluginDesc_c ( std::move ( pLib ) )
  629. {}
  630. PluginQueryTokenFilter_c::PluginQueryTokenFilter_c ( PluginLibRefPtr_c pLib )
  631. : PluginDesc_c ( std::move ( pLib ) )
  632. {}