thorplugin.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519
  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. #ifdef _USE_BINUTILS
  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. #ifdef _USE_BINUTILS
  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. #elif defined (_USE_BINUTILS)
  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. #else
  227. UNIMPLEMENTED;
  228. #endif
  229. }
  230. //-------------------------------------------------------------------------------------------------------------------
  231. bool PluginDll::init(IPluginContextEx * pluginCtx)
  232. {
  233. HINSTANCE h = getInstance();
  234. assertex(h != (HINSTANCE) -1);
  235. EclPluginSetCtxEx pSetCtxEx = (EclPluginSetCtxEx) GetSharedProcedure(h,"setPluginContextEx");
  236. if (pSetCtxEx)
  237. pSetCtxEx(pluginCtx);
  238. else
  239. {
  240. // Older plugins may only support setPluginContext - fall back to that
  241. EclPluginSetCtx pSetCtx = (EclPluginSetCtx) GetSharedProcedure(h,"setPluginContext");
  242. if (pSetCtx)
  243. pSetCtx(pluginCtx);
  244. }
  245. EclPluginDefinition p= (EclPluginDefinition) GetSharedProcedure(h,"getECLPluginDefinition");
  246. if (!p)
  247. return false;
  248. pb.size = sizeof(ECLPluginDefinitionBlockEx);
  249. if (!p(&pb))
  250. {
  251. pb.compatibleVersions = NULL;
  252. pb.size = sizeof(ECLPluginDefinitionBlock);
  253. if (!p(&pb))
  254. return false;
  255. }
  256. return true;
  257. }
  258. bool PluginDll::checkVersion(const char *expected)
  259. {
  260. assertex(expected);
  261. if (stricmp(pb.version, expected) == 0)
  262. return true;
  263. if (pb.compatibleVersions)
  264. {
  265. const char **finger = pb.compatibleVersions;
  266. while (*finger)
  267. {
  268. if (stricmp(*finger, expected) == 0)
  269. return true;
  270. finger++;
  271. }
  272. }
  273. return false;
  274. }
  275. void PluginDll::logLoaded()
  276. {
  277. HelperDll::logLoaded();
  278. DBGLOG("Current reported version is %s", pb.version);
  279. if (pb.compatibleVersions)
  280. {
  281. const char **finger = pb.compatibleVersions;
  282. while (*finger)
  283. {
  284. DBGLOG("Compatible version %s", *finger);
  285. finger++;
  286. }
  287. }
  288. }
  289. extern DLLSERVER_API ILoadedDllEntry * createDllEntry(const char *path, bool isGlobal, const IFileIO *dllFile)
  290. {
  291. Owned<HelperDll> result = new HelperDll(path, dllFile);
  292. if (!result->load(isGlobal, true))
  293. throw MakeStringException(0, "Failed to create ILoadedDllEntry for dll %s", path);
  294. return result.getClear();
  295. }
  296. extern DLLSERVER_API ILoadedDllEntry * createExeDllEntry(const char *path)
  297. {
  298. Owned<HelperDll> result = new HelperDll(path, NULL);
  299. if (!result->loadCurrentExecutable())
  300. throw MakeStringException(0, "Failed to create ILoadedDllEntry for current executable");
  301. return result.getClear();
  302. }
  303. extern DLLSERVER_API bool decompressResource(size32_t len, const void *data, StringBuffer &result)
  304. {
  305. bool hasVersion = len && (*(const byte *)data == 0x80);
  306. MemoryBuffer src;
  307. src.setBuffer(len, const_cast<void *>(data), false);
  308. byte version = 1;
  309. if (hasVersion)
  310. {
  311. src.skip(1);
  312. src.read(version);
  313. }
  314. MemoryBuffer tgt;
  315. switch (version)
  316. {
  317. case 1:
  318. decompressToBuffer(tgt, src);
  319. break;
  320. default:
  321. throwUnexpected();
  322. }
  323. tgt.append((char)0);
  324. unsigned expandedLen = tgt.length();
  325. result.setBuffer(expandedLen, reinterpret_cast<char *>(tgt.detach()), expandedLen-1);
  326. return true;
  327. }
  328. extern DLLSERVER_API void compressResource(MemoryBuffer & compressed, size32_t len, const void *data)
  329. {
  330. byte version = 1;
  331. compressed.append((byte)0x80).append(version);
  332. compressToBuffer(compressed, len, data);
  333. }
  334. extern DLLSERVER_API bool getEmbeddedWorkUnitXML(ILoadedDllEntry *dll, StringBuffer &xml)
  335. {
  336. size32_t len = 0;
  337. const void * data = NULL;
  338. if (!dll->getResource(len, data, "WORKUNIT", 1000))
  339. return false;
  340. return decompressResource(len, data, xml);
  341. }
  342. extern DLLSERVER_API bool getEmbeddedManifestXML(ILoadedDllEntry *dll, StringBuffer &xml)
  343. {
  344. size32_t len = 0;
  345. const void * data = NULL;
  346. if (!dll->getResource(len, data, "MANIFEST", 1000))
  347. return false;
  348. return decompressResource(len, data, xml);
  349. }
  350. extern DLLSERVER_API bool checkEmbeddedWorkUnitXML(ILoadedDllEntry *dll)
  351. {
  352. size32_t len = 0;
  353. const void * data = NULL;
  354. return dll->getResource(len, data, "WORKUNIT", 1000);
  355. }
  356. extern DLLSERVER_API bool getResourceXMLFromFile(const char *filename, const char *type, unsigned id, StringBuffer &xml)
  357. {
  358. MemoryBuffer data;
  359. if (!getResourceFromFile(filename, data, type, id))
  360. return false;
  361. return decompressResource(data.length(), data.toByteArray(), xml);
  362. }
  363. extern DLLSERVER_API bool getWorkunitXMLFromFile(const char *filename, StringBuffer &xml)
  364. {
  365. return getResourceXMLFromFile(filename, "WORKUNIT", 1000, xml);
  366. }
  367. extern DLLSERVER_API bool getManifestXMLFromFile(const char *filename, StringBuffer &xml)
  368. {
  369. return getResourceXMLFromFile(filename, "MANIFEST", 1000, xml);
  370. }
  371. //-------------------------------------------------------------------------------------------------------------------
  372. //-------------------------------------------------------------------------------------------------------------------
  373. bool SafePluginMap::addPlugin(const char *path, const char *dllname)
  374. {
  375. if (!endsWithIgnoreCase(path, SharedObjectExtension))
  376. {
  377. if (trace)
  378. DBGLOG("Ecl plugin %s ignored", path);
  379. return false;
  380. }
  381. try
  382. {
  383. CriticalBlock b(crit);
  384. ILoadedDllEntry *dll = map.getValue(dllname);
  385. if (!dll)
  386. {
  387. Owned<PluginDll> n = new PluginDll(path, NULL);
  388. if (!n->load(true, false) || !n->init(pluginCtx))
  389. throw MakeStringException(0, "Failed to load plugin %s", path);
  390. if (trace)
  391. n->logLoaded();
  392. map.setValue(dllname, n); // note: setValue links arg
  393. return true;
  394. }
  395. return false;
  396. }
  397. catch (IException * e) // MORE - not sure why we don't throw exceptions back here...
  398. {
  399. e->Release();
  400. return false;
  401. }
  402. }
  403. ILoadedDllEntry * SafePluginMap::getPluginDll(const char *id, const char *version, bool checkVersion)
  404. {
  405. CriticalBlock b(crit);
  406. Linked<PluginDll> ret = static_cast<PluginDll *>(map.getValue(id));
  407. if (ret && checkVersion)
  408. {
  409. if (!ret->checkVersion(version))
  410. return NULL;
  411. }
  412. return ret.getLink();
  413. }
  414. void SafePluginMap::loadFromList(const char * pluginsList)
  415. {
  416. const char *pluginDir = pluginsList;
  417. for (;*pluginDir;)
  418. {
  419. StringBuffer thisPlugin;
  420. while (*pluginDir && *pluginDir != ENVSEPCHAR)
  421. thisPlugin.append(*pluginDir++);
  422. if(*pluginDir)
  423. pluginDir++;
  424. if(!thisPlugin.length())
  425. continue;
  426. Owned<IFile> file = createIFile(thisPlugin.str());
  427. if (file->isDirectory() == foundYes)
  428. loadFromDirectory(thisPlugin);
  429. else
  430. {
  431. StringBuffer tail;
  432. splitFilename(thisPlugin, NULL, NULL, &tail, &tail);
  433. addPlugin(thisPlugin, tail.str());
  434. }
  435. }
  436. }
  437. void SafePluginMap::loadFromDirectory(const char * pluginDirectory)
  438. {
  439. const char * mask = "*" SharedObjectExtension;
  440. Owned<IFile> pluginDir = createIFile(pluginDirectory);
  441. Owned<IDirectoryIterator> pluginFiles = pluginDir->directoryFiles(mask,false,false);
  442. ForEach(*pluginFiles)
  443. {
  444. const char *thisPlugin = pluginFiles->query().queryFilename();
  445. StringBuffer tail;
  446. splitFilename(thisPlugin, NULL, NULL, &tail, &tail);
  447. addPlugin(thisPlugin, tail.str());
  448. }
  449. }