thorplugin.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517
  1. /*##############################################################################
  2. Copyright (C) 2011 HPCC Systems.
  3. All rights reserved. This program is free software: you can redistribute it and/or modify
  4. it under the terms of the GNU Affero General Public License as
  5. published by the Free Software Foundation, either version 3 of the
  6. License, or (at your option) any later version.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. GNU Affero General Public License for more details.
  11. You should have received a copy of the GNU Affero General Public License
  12. along with this program. If not, see <http://www.gnu.org/licenses/>.
  13. ############################################################################## */
  14. #include "jexcept.hpp"
  15. #include "jmisc.hpp"
  16. #include "jthread.hpp"
  17. #include "jsocket.hpp"
  18. #include "jprop.hpp"
  19. #include "jdebug.hpp"
  20. #include "jlzw.hpp"
  21. #include "eclrtl.hpp"
  22. #ifndef _WIN32
  23. #include "bfd.h"
  24. #endif
  25. #include "thorplugin.hpp"
  26. void * SimplePluginCtx::ctxMalloc(size_t size)
  27. {
  28. return rtlMalloc(size);
  29. }
  30. void * SimplePluginCtx::ctxRealloc(void * _ptr, size_t size)
  31. {
  32. return rtlRealloc(_ptr, size);
  33. }
  34. void SimplePluginCtx::ctxFree(void * _ptr)
  35. {
  36. rtlFree(_ptr);
  37. }
  38. char * SimplePluginCtx::ctxStrdup(char * _ptr)
  39. {
  40. return strdup(_ptr);
  41. }
  42. int SimplePluginCtx::ctxGetPropInt(const char *propName, int defaultValue) const
  43. {
  44. return defaultValue;
  45. }
  46. const char * SimplePluginCtx::ctxQueryProp(const char *propName) const
  47. {
  48. return NULL;
  49. }
  50. //-------------------------------------------------------------------------------------------------------------------
  51. class HelperDll : public CInterface, implements ILoadedDllEntry
  52. {
  53. SharedObject so;
  54. StringBuffer version;
  55. StringBuffer fileLocation;
  56. StringAttr name;
  57. Linked<const IFileIO> dllFile;
  58. bool logLoad;
  59. public:
  60. IMPLEMENT_IINTERFACE;
  61. HelperDll(const char *_name, const IFileIO *dllFile);
  62. ~HelperDll();
  63. //interface ILoadedDllEntry
  64. virtual HINSTANCE getInstance() const;
  65. virtual void * getEntry(const char * name) const;
  66. virtual bool IsShared();
  67. virtual const char * queryVersion() const;
  68. virtual const char * queryName() const;
  69. virtual const byte * getResource(unsigned id) const;
  70. virtual bool getResource(size32_t & len, const void * & data, const char * type, unsigned id) const;
  71. bool load(bool isGlobal, bool raiseOnError);
  72. bool loadCurrentExecutable();
  73. virtual void logLoaded();
  74. virtual bool checkVersion(const char *expected);
  75. };
  76. class PluginDll : public HelperDll
  77. {
  78. ECLPluginDefinitionBlockEx pb;
  79. public:
  80. PluginDll(const char *_name, const IFileIO *_dllFile) : HelperDll(_name, _dllFile) {}
  81. bool init(IPluginContextEx * pluginCtx);
  82. virtual bool checkVersion(const char *expected);
  83. virtual void logLoaded();
  84. };
  85. HelperDll::HelperDll(const char *_name, const IFileIO *_dllFile)
  86. : name(_name), dllFile(_dllFile)
  87. {
  88. logLoad = false;
  89. }
  90. bool HelperDll::load(bool isGlobal, bool raiseOnError)
  91. {
  92. if (!so.load(name, isGlobal, raiseOnError))
  93. return false;
  94. return true;
  95. }
  96. bool HelperDll::loadCurrentExecutable()
  97. {
  98. if (!so.loadCurrentExecutable())
  99. return false;
  100. return true;
  101. }
  102. HelperDll::~HelperDll()
  103. {
  104. if (logLoad)
  105. DBGLOG("Unloading dll %s", name.get());
  106. }
  107. HINSTANCE HelperDll::getInstance() const
  108. {
  109. return so.getInstanceHandle();
  110. }
  111. void * HelperDll::getEntry(const char * name) const
  112. {
  113. return GetSharedProcedure(so.getInstanceHandle(), name);
  114. }
  115. bool HelperDll::IsShared()
  116. {
  117. return CInterface::IsShared();
  118. }
  119. const char * HelperDll::queryVersion() const
  120. {
  121. return version.str();
  122. }
  123. void HelperDll::logLoaded()
  124. {
  125. logLoad = true;
  126. DBGLOG("Loaded DLL %s", name.get());
  127. }
  128. bool HelperDll::checkVersion(const char *expected)
  129. {
  130. return true;
  131. }
  132. const char * HelperDll::queryName() const
  133. {
  134. return name.get();
  135. }
  136. const byte * HelperDll::getResource(unsigned id) const
  137. {
  138. #ifdef _WIN32
  139. HINSTANCE dllHandle = so.getInstanceHandle();
  140. HRSRC hrsrc = FindResource(dllHandle, MAKEINTRESOURCE(id), "BIGSTRING");
  141. if (hrsrc)
  142. return (const byte *) LoadResource(dllHandle, hrsrc);
  143. return NULL;
  144. #else
  145. StringBuffer resourceName;
  146. resourceName.appendf("BIGSTRING_%d_txt_start", id);
  147. return (const byte *) getEntry(resourceName.str());
  148. #endif
  149. }
  150. bool HelperDll::getResource(size32_t & len, const void * & data, const char * type, unsigned id) const
  151. {
  152. #ifdef _WIN32
  153. HINSTANCE dllHandle = so.getInstanceHandle();
  154. HRSRC hrsrc = FindResource(dllHandle, MAKEINTRESOURCE(id), type);
  155. if (!hrsrc)
  156. return false;
  157. len = SizeofResource(dllHandle, hrsrc);
  158. data = (const byte *) LoadResource(dllHandle, hrsrc);
  159. return true;
  160. #else
  161. StringBuffer baseName;
  162. baseName.append(type).append("_").append(id);
  163. StringBuffer symName, sizeName;
  164. symName.append(baseName).append("_txt_start");
  165. sizeName.append(baseName).append("_txt_size");
  166. data = (const void *) getEntry(symName.str());
  167. if (!data)
  168. return false;
  169. len = (unsigned)(memsize_t)getEntry(sizeName.str());
  170. return true;
  171. #endif
  172. }
  173. #ifndef _WIN32
  174. struct SecScanParam
  175. {
  176. MemoryBuffer &result;
  177. const char *sectionName;
  178. SecScanParam(MemoryBuffer &_result, const char *_sectionName)
  179. : result(_result), sectionName(_sectionName)
  180. {
  181. }
  182. };
  183. static void secscan (bfd *file, sec_ptr sec, void *userParam)
  184. {
  185. SecScanParam *param = (SecScanParam *) userParam;
  186. if (strcmp(param->sectionName, bfd_section_name (file, sec))==0)
  187. {
  188. bfd_size_type size = bfd_section_size (file, sec);
  189. void *data = (void *) param->result.reserve(size);
  190. bfd_get_section_contents(file, sec, data, 0, size);
  191. }
  192. }
  193. #endif
  194. extern bool getResourceFromFile(const char *filename, MemoryBuffer &data, const char * type, unsigned id)
  195. {
  196. #ifdef _WIN32
  197. HINSTANCE dllHandle = LoadLibraryEx(filename, NULL, LOAD_LIBRARY_AS_DATAFILE|LOAD_LIBRARY_AS_IMAGE_RESOURCE);
  198. if (dllHandle == NULL)
  199. dllHandle = LoadLibraryEx(filename, NULL, LOAD_LIBRARY_AS_DATAFILE); // the LOAD_LIBRARY_AS_IMAGE_RESOURCE flag is not supported on all versions of Windows
  200. if (dllHandle == NULL)
  201. {
  202. DBGLOG("Failed to load library %s: %d", filename, GetLastError());
  203. return false;
  204. }
  205. HRSRC hrsrc = FindResource(dllHandle, MAKEINTRESOURCE(id), type);
  206. if (!hrsrc)
  207. return false;
  208. size32_t len = SizeofResource(dllHandle, hrsrc);
  209. const void *rdata = (const void *) LoadResource(dllHandle, hrsrc);
  210. data.append(len, rdata);
  211. FreeLibrary(dllHandle);
  212. return true;
  213. #else
  214. bfd_init ();
  215. bfd *file = bfd_openr(filename, NULL);
  216. if (file)
  217. {
  218. StringBuffer sectionName;
  219. sectionName.append(type).append("_").append(id).append(".data");
  220. SecScanParam param(data, sectionName.str());
  221. if (bfd_check_format (file, bfd_object))
  222. bfd_map_over_sections (file, secscan, &param);
  223. bfd_close (file);
  224. }
  225. return data.length() != 0;
  226. #endif
  227. }
  228. //-------------------------------------------------------------------------------------------------------------------
  229. bool PluginDll::init(IPluginContextEx * pluginCtx)
  230. {
  231. HINSTANCE h = getInstance();
  232. assertex(h != (HINSTANCE) -1);
  233. EclPluginSetCtxEx pSetCtxEx = (EclPluginSetCtxEx) GetSharedProcedure(h,"setPluginContextEx");
  234. if (pSetCtxEx)
  235. pSetCtxEx(pluginCtx);
  236. else
  237. {
  238. // Older plugins may only support setPluginContext - fall back to that
  239. EclPluginSetCtx pSetCtx = (EclPluginSetCtx) GetSharedProcedure(h,"setPluginContext");
  240. if (pSetCtx)
  241. pSetCtx(pluginCtx);
  242. }
  243. EclPluginDefinition p= (EclPluginDefinition) GetSharedProcedure(h,"getECLPluginDefinition");
  244. if (!p)
  245. return false;
  246. pb.size = sizeof(ECLPluginDefinitionBlockEx);
  247. if (!p(&pb))
  248. {
  249. pb.compatibleVersions = NULL;
  250. pb.size = sizeof(ECLPluginDefinitionBlock);
  251. if (!p(&pb))
  252. return false;
  253. }
  254. return true;
  255. }
  256. bool PluginDll::checkVersion(const char *expected)
  257. {
  258. assertex(expected);
  259. if (stricmp(pb.version, expected) == 0)
  260. return true;
  261. if (pb.compatibleVersions)
  262. {
  263. const char **finger = pb.compatibleVersions;
  264. while (*finger)
  265. {
  266. if (stricmp(*finger, expected) == 0)
  267. return true;
  268. finger++;
  269. }
  270. }
  271. return false;
  272. }
  273. void PluginDll::logLoaded()
  274. {
  275. HelperDll::logLoaded();
  276. DBGLOG("Current reported version is %s", pb.version);
  277. if (pb.compatibleVersions)
  278. {
  279. const char **finger = pb.compatibleVersions;
  280. while (*finger)
  281. {
  282. DBGLOG("Compatible version %s", *finger);
  283. finger++;
  284. }
  285. }
  286. }
  287. extern DLLSERVER_API ILoadedDllEntry * createDllEntry(const char *path, bool isGlobal, const IFileIO *dllFile)
  288. {
  289. Owned<HelperDll> result = new HelperDll(path, dllFile);
  290. if (!result->load(isGlobal, true))
  291. throw MakeStringException(0, "Failed to create ILoadedDllEntry for dll %s", path);
  292. return result.getClear();
  293. }
  294. extern DLLSERVER_API ILoadedDllEntry * createExeDllEntry(const char *path)
  295. {
  296. Owned<HelperDll> result = new HelperDll(path, NULL);
  297. if (!result->loadCurrentExecutable())
  298. throw MakeStringException(0, "Failed to create ILoadedDllEntry for current executable");
  299. return result.getClear();
  300. }
  301. extern DLLSERVER_API bool decompressResource(size32_t len, const void *data, StringBuffer &result)
  302. {
  303. bool hasVersion = len && (*(const byte *)data == 0x80);
  304. MemoryBuffer src;
  305. src.setBuffer(len, const_cast<void *>(data), false);
  306. byte version = 1;
  307. if (hasVersion)
  308. {
  309. src.skip(1);
  310. src.read(version);
  311. }
  312. MemoryBuffer tgt;
  313. switch (version)
  314. {
  315. case 1:
  316. decompressToBuffer(tgt, src);
  317. break;
  318. default:
  319. throwUnexpected();
  320. }
  321. tgt.append((char)0);
  322. unsigned expandedLen = tgt.length();
  323. result.setBuffer(expandedLen, reinterpret_cast<char *>(tgt.detach()), expandedLen-1);
  324. return true;
  325. }
  326. extern DLLSERVER_API void compressResource(MemoryBuffer & compressed, size32_t len, const void *data)
  327. {
  328. byte version = 1;
  329. compressed.append((byte)0x80).append(version);
  330. compressToBuffer(compressed, len, data);
  331. }
  332. extern DLLSERVER_API bool getEmbeddedWorkUnitXML(ILoadedDllEntry *dll, StringBuffer &xml)
  333. {
  334. size32_t len = 0;
  335. const void * data = NULL;
  336. if (!dll->getResource(len, data, "WORKUNIT", 1000))
  337. return false;
  338. return decompressResource(len, data, xml);
  339. }
  340. extern DLLSERVER_API bool getEmbeddedSoapInfoXML(ILoadedDllEntry *dll, StringBuffer &xml)
  341. {
  342. size32_t len = 0;
  343. const void * data = NULL;
  344. if (!dll->getResource(len, data, "SOAPINFO", 1000))
  345. return false;
  346. return decompressResource(len, data, xml);
  347. }
  348. extern DLLSERVER_API bool checkEmbeddedWorkUnitXML(ILoadedDllEntry *dll)
  349. {
  350. size32_t len = 0;
  351. const void * data = NULL;
  352. return dll->getResource(len, data, "WORKUNIT", 1000);
  353. }
  354. extern DLLSERVER_API bool getResourceXMLFromFile(const char *filename, const char *type, unsigned id, StringBuffer &xml)
  355. {
  356. MemoryBuffer data;
  357. if (!getResourceFromFile(filename, data, type, id))
  358. return false;
  359. return decompressResource(data.length(), data.toByteArray(), xml);
  360. }
  361. extern DLLSERVER_API bool getWorkunitXMLFromFile(const char *filename, StringBuffer &xml)
  362. {
  363. return getResourceXMLFromFile(filename, "WORKUNIT", 1000, xml);
  364. }
  365. extern DLLSERVER_API bool getSoapInfoXMLFromFile(const char *filename, StringBuffer &xml)
  366. {
  367. return getResourceXMLFromFile(filename, "SOAPINFO", 1000, xml);
  368. }
  369. //-------------------------------------------------------------------------------------------------------------------
  370. //-------------------------------------------------------------------------------------------------------------------
  371. bool SafePluginMap::addPlugin(const char *path, const char *dllname)
  372. {
  373. if (!endsWithIgnoreCase(path, SharedObjectExtension))
  374. {
  375. if (trace)
  376. DBGLOG("Ecl plugin %s ignored", path);
  377. return false;
  378. }
  379. try
  380. {
  381. CriticalBlock b(crit);
  382. ILoadedDllEntry *dll = map.getValue(dllname);
  383. if (!dll)
  384. {
  385. Owned<PluginDll> n = new PluginDll(path, NULL);
  386. if (!n->load(true, false) || !n->init(pluginCtx))
  387. throw MakeStringException(0, "Failed to load plugin %s", path);
  388. if (trace)
  389. n->logLoaded();
  390. map.setValue(dllname, n); // note: setValue links arg
  391. return true;
  392. }
  393. return false;
  394. }
  395. catch (IException * e) // MORE - not sure why we don't throw exceptions back here...
  396. {
  397. e->Release();
  398. return false;
  399. }
  400. }
  401. ILoadedDllEntry * SafePluginMap::getPluginDll(const char *id, const char *version, bool checkVersion)
  402. {
  403. CriticalBlock b(crit);
  404. Linked<PluginDll> ret = static_cast<PluginDll *>(map.getValue(id));
  405. if (ret && checkVersion)
  406. {
  407. if (!ret->checkVersion(version))
  408. return NULL;
  409. }
  410. return ret.getLink();
  411. }
  412. void SafePluginMap::loadFromList(const char * pluginsList)
  413. {
  414. const char *pluginDir = pluginsList;
  415. for (;*pluginDir;)
  416. {
  417. StringBuffer thisPlugin;
  418. while (*pluginDir && *pluginDir != ENVSEPCHAR)
  419. thisPlugin.append(*pluginDir++);
  420. if(*pluginDir)
  421. pluginDir++;
  422. if(!thisPlugin.length())
  423. continue;
  424. Owned<IFile> file = createIFile(thisPlugin.str());
  425. if (file->isDirectory() == foundYes)
  426. loadFromDirectory(thisPlugin);
  427. else
  428. {
  429. StringBuffer tail;
  430. splitFilename(thisPlugin, NULL, NULL, &tail, &tail);
  431. addPlugin(thisPlugin, tail.str());
  432. }
  433. }
  434. }
  435. void SafePluginMap::loadFromDirectory(const char * pluginDirectory)
  436. {
  437. const char * mask = "*" SharedObjectExtension;
  438. Owned<IFile> pluginDir = createIFile(pluginDirectory);
  439. Owned<IDirectoryIterator> pluginFiles = pluginDir->directoryFiles(mask,false,false);
  440. ForEach(*pluginFiles)
  441. {
  442. const char *thisPlugin = pluginFiles->query().queryFilename();
  443. StringBuffer tail;
  444. splitFilename(thisPlugin, NULL, NULL, &tail, &tail);
  445. addPlugin(thisPlugin, tail.str());
  446. }
  447. }