thorplugin.cpp 15 KB

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