thorplugin.cpp 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920
  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 "jregexp.hpp"
  20. #include "jlzw.hpp"
  21. #include "eclrtl.hpp"
  22. #if defined(__APPLE__)
  23. #include <mach-o/getsect.h>
  24. #include <sys/mman.h>
  25. #include <sys/stat.h>
  26. #elif !defined(_WIN32)
  27. #include <sys/mman.h>
  28. #include <sys/stat.h>
  29. #include <elf.h>
  30. #endif
  31. #include "thorplugin.hpp"
  32. void * SimplePluginCtx::ctxMalloc(size_t size)
  33. {
  34. return rtlMalloc(size);
  35. }
  36. void * SimplePluginCtx::ctxRealloc(void * _ptr, size_t size)
  37. {
  38. return rtlRealloc(_ptr, size);
  39. }
  40. void SimplePluginCtx::ctxFree(void * _ptr)
  41. {
  42. rtlFree(_ptr);
  43. }
  44. char * SimplePluginCtx::ctxStrdup(char * _ptr)
  45. {
  46. return strdup(_ptr);
  47. }
  48. int SimplePluginCtx::ctxGetPropInt(const char *propName, int defaultValue) const
  49. {
  50. return defaultValue;
  51. }
  52. const char * SimplePluginCtx::ctxQueryProp(const char *propName) const
  53. {
  54. return NULL;
  55. }
  56. //-------------------------------------------------------------------------------------------------------------------
  57. static bool getResourceFromMappedFile(const char * filename, const byte * start_addr, size32_t & lenData, const void * & data, const char * type, unsigned id)
  58. {
  59. #if defined(_WIN32)
  60. throwUnexpected();
  61. #elif defined(__APPLE__)
  62. VStringBuffer sectname("%s_%u", type, id);
  63. // The first bytes are the Mach-O header
  64. const struct mach_header_64 *mh = (const struct mach_header_64 *) start_addr;
  65. if (mh->magic != MH_MAGIC_64)
  66. {
  67. DBGLOG("Failed to extract resource %s: Does not appear to be a Mach-O 64-bit binary", filename);
  68. return false;
  69. }
  70. unsigned long len = 0;
  71. data = getsectiondata(mh, "__TEXT", sectname.str(), &len);
  72. lenData = (size32_t)len;
  73. return true;
  74. #elif defined (__64BIT__)
  75. // The first bytes are the ELF header
  76. const Elf64_Ehdr * hdr = (const Elf64_Ehdr *) start_addr;
  77. if (memcmp(hdr->e_ident, ELFMAG, SELFMAG) != 0)
  78. {
  79. DBGLOG("Failed to extract resource %s: Does not appear to be a ELF binary", filename);
  80. return false;
  81. }
  82. if (hdr->e_ident[EI_CLASS] != ELFCLASS64)
  83. {
  84. DBGLOG("Failed to extract resource %s: Does not appear to be a ELF 64-bit binary", filename);
  85. return false;
  86. }
  87. //Check that there is a symbol table for the sections.
  88. if (hdr->e_shstrndx == SHN_UNDEF)
  89. {
  90. DBGLOG("Failed to extract resource %s: Does not include a section symbol table", filename);
  91. return false;
  92. }
  93. //Now walk the sections comparing the section names
  94. Elf64_Half numSections = hdr->e_shnum;
  95. const Elf64_Shdr * sectionHeaders = reinterpret_cast<const Elf64_Shdr *>(start_addr + hdr->e_shoff);
  96. const Elf64_Shdr & symbolTableSection = sectionHeaders[hdr->e_shstrndx];
  97. const char * symbolTable = (const char *)start_addr + symbolTableSection.sh_offset;
  98. VStringBuffer sectname("%s_%u", type, id);
  99. for (unsigned iSect= 0; iSect < numSections; iSect++)
  100. {
  101. const Elf64_Shdr & section = sectionHeaders[iSect];
  102. const char * sectionName = symbolTable + section.sh_name;
  103. if (streq(sectionName, sectname))
  104. {
  105. lenData = (size32_t)section.sh_size;
  106. data = start_addr + section.sh_offset;
  107. return true;
  108. }
  109. }
  110. return false;
  111. #else
  112. // The first bytes are the ELF header
  113. const Elf32_Ehdr * hdr = (const Elf32_Ehdr *) start_addr;
  114. if (memcmp(hdr->e_ident, ELFMAG, SELFMAG) != 0)
  115. {
  116. DBGLOG("Failed to extract resource %s: Does not appear to be a ELF binary", filename);
  117. return false;
  118. }
  119. if (hdr->e_ident[EI_CLASS] != ELFCLASS32)
  120. {
  121. DBGLOG("Failed to extract resource %s: Does not appear to be a ELF 32-bit binary", filename);
  122. return false;
  123. }
  124. //Check that there is a symbol table for the sections.
  125. if (hdr->e_shstrndx == SHN_UNDEF)
  126. {
  127. DBGLOG("Failed to extract resource %s: Does not include a section symbol table", filename);
  128. return false;
  129. }
  130. //Now walk the sections comparing the section names
  131. Elf32_Half numSections = hdr->e_shnum;
  132. const Elf32_Shdr * sectionHeaders = reinterpret_cast<const Elf32_Shdr *>(start_addr + hdr->e_shoff);
  133. const Elf32_Shdr & symbolTableSection = sectionHeaders[hdr->e_shstrndx];
  134. const char * symbolTable = (const char *)start_addr + symbolTableSection.sh_offset;
  135. VStringBuffer sectname("%s_%u", type, id);
  136. for (unsigned iSect= 0; iSect < numSections; iSect++)
  137. {
  138. const Elf32_Shdr & section = sectionHeaders[iSect];
  139. const char * sectionName = symbolTable + section.sh_name;
  140. if (streq(sectionName, sectname))
  141. {
  142. lenData = (size32_t)section.sh_size;
  143. data = start_addr + section.sh_offset;
  144. return true;
  145. }
  146. }
  147. return false;
  148. #endif
  149. }
  150. static bool getResourceFromMappedFile(const char * filename, const byte * start_addr, MemoryBuffer & result, const char * type, unsigned id)
  151. {
  152. size32_t len = 0;
  153. const void * data = nullptr;
  154. bool ok = getResourceFromMappedFile(filename, start_addr, len, data, type, id);
  155. if (ok)
  156. result.append(len, data);
  157. return ok;
  158. }
  159. extern bool getResourceFromFile(const char *filename, MemoryBuffer &data, const char * type, unsigned id)
  160. {
  161. #ifdef _WIN32
  162. HINSTANCE dllHandle = LoadLibraryEx(filename, NULL, LOAD_LIBRARY_AS_DATAFILE|LOAD_LIBRARY_AS_IMAGE_RESOURCE);
  163. if (dllHandle == NULL)
  164. dllHandle = LoadLibraryEx(filename, NULL, LOAD_LIBRARY_AS_DATAFILE); // the LOAD_LIBRARY_AS_IMAGE_RESOURCE flag is not supported on all versions of Windows
  165. if (dllHandle == NULL)
  166. {
  167. DBGLOG("Failed to load library %s: %d", filename, GetLastError());
  168. return false;
  169. }
  170. HRSRC hrsrc = FindResource(dllHandle, MAKEINTRESOURCE(id), type);
  171. if (!hrsrc)
  172. return false;
  173. size32_t len = SizeofResource(dllHandle, hrsrc);
  174. const void *rdata = (const void *) LoadResource(dllHandle, hrsrc);
  175. data.append(len, rdata);
  176. FreeLibrary(dllHandle);
  177. return true;
  178. #else
  179. struct stat stat_buf;
  180. VStringBuffer sectname("%s_%u", type, id);
  181. int fd = open(filename, O_RDONLY);
  182. if (fd == -1)
  183. {
  184. DBGLOG("Failed to load library %s: %d", filename, errno);
  185. return false;
  186. }
  187. bool ok = false;
  188. if (fstat(fd, &stat_buf) != -1)
  189. {
  190. __uint64 size = stat_buf.st_size;
  191. const byte *start_addr = (const byte *) mmap(0, size, PROT_READ, MAP_FILE | MAP_PRIVATE, fd, 0);
  192. if (start_addr == MAP_FAILED)
  193. {
  194. DBGLOG("Failed to load library %s: %d", filename, errno);
  195. }
  196. else
  197. {
  198. ok = getResourceFromMappedFile(filename, start_addr, data, type, id);
  199. munmap((void *)start_addr, size);
  200. }
  201. }
  202. else
  203. DBGLOG("Failed to load library %s: %d", filename, errno);
  204. close(fd);
  205. return ok;
  206. #endif
  207. }
  208. //-------------------------------------------------------------------------------------------------------------------
  209. class ManifestFileList : public MappingBase
  210. {
  211. StringArray filenames;
  212. StringAttr type;
  213. StringAttr dir;
  214. void recursiveRemoveDirectory(const char *fullPath)
  215. {
  216. if (rmdir(fullPath) == 0 && !streq(fullPath, dir))
  217. {
  218. StringBuffer head;
  219. splitFilename(fullPath, &head, &head, NULL, NULL);
  220. if (head.length() > 1)
  221. {
  222. head.setLength(head.length()-1);
  223. recursiveRemoveDirectory(head);
  224. }
  225. }
  226. }
  227. void removeFileAndEmptyParents(const char *fullFileName)
  228. {
  229. remove(fullFileName);
  230. StringBuffer path;
  231. splitFilename(fullFileName, &path, &path, NULL, NULL);
  232. if (path.length() > 1)
  233. {
  234. path.setLength(path.length()-1);
  235. recursiveRemoveDirectory(path.str());
  236. }
  237. }
  238. public:
  239. ManifestFileList(const char *_type, const char *_dir) : type(_type), dir(_dir) {}
  240. ~ManifestFileList()
  241. {
  242. ForEachItemIn(idx, filenames)
  243. {
  244. removeFileAndEmptyParents(filenames.item(idx));
  245. }
  246. rmdir(dir); // If the specified temporary directory is now empty, remove it.
  247. }
  248. void append(const char *filename)
  249. {
  250. assertex(strncmp(filename, dir, strlen(dir))==0);
  251. filenames.append(filename);
  252. }
  253. inline const StringArray &queryFileNames() { return filenames; }
  254. virtual const void * getKey() const { return type; }
  255. };
  256. class HelperDll : implements ILoadedDllEntry, public CInterface
  257. {
  258. SharedObject so;
  259. Linked<const IFileIO> dllFile;
  260. Owned<IMemoryMappedFile> mappedDll;
  261. mutable std::atomic<IPropertyTree *> manifest {nullptr};
  262. mutable CriticalSection manifestLock;
  263. mutable StringMapOf<ManifestFileList> manifestFiles;
  264. protected:
  265. StringAttr name;
  266. bool logLoad;
  267. public:
  268. IMPLEMENT_IINTERFACE;
  269. HelperDll(const char *_name, const IFileIO *dllFile);
  270. ~HelperDll();
  271. //interface ILoadedDllEntry
  272. virtual HINSTANCE getInstance() const;
  273. virtual void * getEntry(const char * name) const;
  274. virtual bool IsShared();
  275. virtual const char * queryVersion() const;
  276. virtual const char * queryName() const;
  277. virtual const byte * getResource(unsigned id) const;
  278. virtual bool getResource(size32_t & len, const void * & data, const char * type, unsigned id, bool trace) const;
  279. virtual IPropertyTree &queryManifest() const override;
  280. virtual const StringArray &queryManifestFiles(const char *type, const char *wuid) const override;
  281. bool load(bool isGlobal, bool raiseOnError);
  282. bool loadCurrentExecutable();
  283. bool loadResources();
  284. virtual void logLoaded();
  285. virtual bool checkVersion(const char *expected);
  286. };
  287. class PluginDll : public HelperDll
  288. {
  289. ECLPluginDefinitionBlockEx pb;
  290. public:
  291. PluginDll(const char *_name, const IFileIO *_dllFile) : HelperDll(_name, _dllFile) {}
  292. bool init(IPluginContextEx * pluginCtx);
  293. virtual bool checkVersion(const char *expected) override;
  294. virtual void logLoaded() override;
  295. };
  296. HelperDll::HelperDll(const char *_name, const IFileIO *_dllFile)
  297. : name(_name), dllFile(_dllFile), manifestFiles(false)
  298. {
  299. logLoad = false;
  300. }
  301. bool HelperDll::load(bool isGlobal, bool raiseOnError)
  302. {
  303. if (!so.load(name, isGlobal, raiseOnError))
  304. return false;
  305. return true;
  306. }
  307. bool HelperDll::loadResources()
  308. {
  309. #ifdef _WIN32
  310. return so.loadResources(name);
  311. #else
  312. Owned<IFile> file = createIFile(name);
  313. mappedDll.setown(file->openMemoryMapped());
  314. return mappedDll != nullptr;
  315. #endif
  316. }
  317. bool HelperDll::loadCurrentExecutable()
  318. {
  319. if (!so.loadCurrentExecutable())
  320. return false;
  321. return true;
  322. }
  323. HelperDll::~HelperDll()
  324. {
  325. if (logLoad)
  326. DBGLOG("Unloading dll %s", name.get());
  327. ::Release(manifest.load(std::memory_order_relaxed));
  328. }
  329. HINSTANCE HelperDll::getInstance() const
  330. {
  331. if (!so.loaded())
  332. throw MakeStringException(0, "Dll %s only loaded for resources", name.str());
  333. return so.getInstanceHandle();
  334. }
  335. void * HelperDll::getEntry(const char * entry) const
  336. {
  337. if (!so.loaded())
  338. throw MakeStringException(0, "Dll %s only loaded for resources", name.str());
  339. return so.getEntry(entry);
  340. }
  341. bool HelperDll::IsShared()
  342. {
  343. return CInterface::IsShared();
  344. }
  345. const char * HelperDll::queryVersion() const
  346. {
  347. return "";
  348. }
  349. void HelperDll::logLoaded()
  350. {
  351. logLoad = true;
  352. DBGLOG("Loaded DLL %s", name.get());
  353. }
  354. bool HelperDll::checkVersion(const char *expected)
  355. {
  356. return true;
  357. }
  358. const char * HelperDll::queryName() const
  359. {
  360. return name.get();
  361. }
  362. const byte * HelperDll::getResource(unsigned id) const
  363. {
  364. if (so.loaded())
  365. {
  366. #ifdef _WIN32
  367. HINSTANCE dllHandle = so.getInstanceHandle();
  368. HRSRC hrsrc = FindResource(dllHandle, MAKEINTRESOURCE(id), "BIGSTRING");
  369. if (hrsrc)
  370. return (const byte *)LoadResource(dllHandle, hrsrc);
  371. return NULL;
  372. #else
  373. StringBuffer resourceName;
  374. resourceName.appendf("BIGSTRING_%d_txt_start", id);
  375. return (const byte *)getEntry(resourceName.str());
  376. #endif
  377. }
  378. else
  379. {
  380. size32_t len;
  381. const void * data;
  382. if (getResource(len, data, "BIGSTRING", id, false))
  383. return (const byte *)data;
  384. return nullptr;
  385. }
  386. }
  387. const byte resourceHeaderVersion=1;
  388. const size32_t resourceHeaderLength = sizeof(byte) + sizeof(byte) + sizeof(bool) + sizeof(size32_t);
  389. bool HelperDll::getResource(size32_t & len, const void * & data, const char * type, unsigned id, bool trace) const
  390. {
  391. if (so.loaded())
  392. {
  393. #ifdef _WIN32
  394. HINSTANCE dllHandle = so.getInstanceHandle();
  395. HRSRC hrsrc = FindResource(dllHandle, MAKEINTRESOURCE(id), type);
  396. if (!hrsrc)
  397. return false;
  398. len = SizeofResource(dllHandle, hrsrc);
  399. data = (const byte *)LoadResource(dllHandle, hrsrc);
  400. return true;
  401. #else
  402. StringBuffer symName;
  403. symName.append(type).append("_").append(id).append("_txt_start");
  404. data = (const void *)getEntry(symName.str());
  405. if (!data)
  406. {
  407. if (trace)
  408. printf("Failed to locate symbol %s\n", symName.str());
  409. return false;
  410. }
  411. byte bom;
  412. byte version;
  413. bool compressed;
  414. MemoryBuffer mb;
  415. mb.setBuffer(resourceHeaderLength, const_cast<void *>(data));
  416. mb.read(bom);
  417. if (bom != 0x80)
  418. return false;
  419. mb.read(version);
  420. if (version > resourceHeaderVersion)
  421. return false;
  422. mb.read(compressed).read(len);
  423. len += resourceHeaderLength;
  424. return true;
  425. #endif
  426. }
  427. else
  428. {
  429. #ifdef _WIN32
  430. return false;
  431. #endif
  432. if (!mappedDll)
  433. return false;
  434. return getResourceFromMappedFile(name, mappedDll->base(), len, data, type, id);
  435. }
  436. }
  437. IPropertyTree &HelperDll::queryManifest() const
  438. {
  439. return *querySingleton(manifest, manifestLock, [this]{ return getEmbeddedManifestPTree(this); });
  440. }
  441. const StringArray &HelperDll::queryManifestFiles(const char *type, const char *wuid) const
  442. {
  443. CriticalBlock b(manifestLock);
  444. Linked<ManifestFileList> list = manifestFiles.find(type);
  445. if (!list)
  446. {
  447. // The temporary path we unpack to is based on so file's current location and workunit
  448. // MORE - this is good for deployed cases, may not be so good for standalone executables.
  449. StringBuffer tempDir;
  450. splitFilename(name, &tempDir, &tempDir, &tempDir, nullptr);
  451. list.setown(new ManifestFileList(type, tempDir));
  452. tempDir.append(".tmp").append(PATHSEPCHAR).append(wuid);
  453. VStringBuffer xpath("Resource[@type='%s']", type);
  454. Owned<IPropertyTreeIterator> resourceFiles = queryManifest().getElements(xpath.str());
  455. ForEach(*resourceFiles)
  456. {
  457. IPropertyTree &resourceFile = resourceFiles->query();
  458. unsigned id = resourceFile.getPropInt("@id", 0);
  459. size32_t len = 0;
  460. const void *data = nullptr;
  461. if (!getResource(len, data, type, id, false))
  462. throwUnexpected();
  463. MemoryBuffer decompressed;
  464. if (resourceFile.getPropBool("@compressed"))
  465. {
  466. // MORE - would be better to try to spot files that are not worth recompressing (like jar files)?
  467. decompressResource(len, data, decompressed);
  468. data = decompressed.toByteArray();
  469. len = decompressed.length();
  470. }
  471. else
  472. {
  473. // Data is preceded by the resource header
  474. // MORE - does this depend on whether @header is set? is that what @header means?
  475. data = ((const byte *) data) + resourceHeaderLength;
  476. len -= resourceHeaderLength;
  477. }
  478. StringBuffer extractName(tempDir);
  479. extractName.append(PATHSEPCHAR);
  480. if (resourceFile.hasProp("@filename"))
  481. resourceFile.getProp("@filename", extractName);
  482. else
  483. extractName.append(id).append('.').append(type);
  484. recursiveCreateDirectoryForFile(extractName);
  485. OwnedIFile f = createIFile(extractName);
  486. OwnedIFileIO o = f->open(IFOcreate);
  487. assertex(o.get() != nullptr);
  488. o->write(0, len, data);
  489. list->append(extractName);
  490. }
  491. manifestFiles.replaceOwn(*list.getLink());
  492. }
  493. return list->queryFileNames();
  494. }
  495. //-------------------------------------------------------------------------------------------------------------------
  496. bool PluginDll::init(IPluginContextEx * pluginCtx)
  497. {
  498. HINSTANCE h = getInstance();
  499. assertex(h != (HINSTANCE) -1);
  500. EclPluginSetCtxEx pSetCtxEx = (EclPluginSetCtxEx) GetSharedProcedure(h,"setPluginContextEx");
  501. if (pSetCtxEx)
  502. pSetCtxEx(pluginCtx);
  503. else
  504. {
  505. // Older plugins may only support setPluginContext - fall back to that
  506. EclPluginSetCtx pSetCtx = (EclPluginSetCtx) GetSharedProcedure(h,"setPluginContext");
  507. if (pSetCtx)
  508. pSetCtx(pluginCtx);
  509. }
  510. EclPluginDefinition p= (EclPluginDefinition) GetSharedProcedure(h,"getECLPluginDefinition");
  511. if (!p)
  512. return false;
  513. pb.size = sizeof(ECLPluginDefinitionBlockEx);
  514. if (!p(&pb))
  515. {
  516. pb.compatibleVersions = NULL;
  517. pb.size = sizeof(ECLPluginDefinitionBlock);
  518. if (!p(&pb))
  519. return false;
  520. }
  521. return true;
  522. }
  523. bool PluginDll::checkVersion(const char *expected)
  524. {
  525. assertex(expected);
  526. if (stricmp(pb.version, expected) == 0)
  527. return true;
  528. if (pb.compatibleVersions)
  529. {
  530. const char **finger = pb.compatibleVersions;
  531. while (*finger)
  532. {
  533. if (stricmp(*finger, expected) == 0)
  534. return true;
  535. finger++;
  536. }
  537. }
  538. return false;
  539. }
  540. void PluginDll::logLoaded()
  541. {
  542. logLoad = true;
  543. DBGLOG("Loaded DLL %s [%s]", name.get(), pb.version);
  544. }
  545. extern DLLSERVER_API ILoadedDllEntry * createDllEntry(const char *path, bool isGlobal, const IFileIO *dllFile, bool resourcesOnly)
  546. {
  547. Owned<HelperDll> result = new HelperDll(path, dllFile);
  548. bool ok;
  549. if (!resourcesOnly)
  550. ok = result->load(isGlobal, true);
  551. else
  552. ok = result->loadResources();
  553. if (!ok)
  554. throw MakeStringException(0, "Failed to create ILoadedDllEntry for dll %s", path);
  555. return result.getClear();
  556. }
  557. extern DLLSERVER_API ILoadedDllEntry * createExeDllEntry(const char *path)
  558. {
  559. Owned<HelperDll> result = new HelperDll(path, NULL);
  560. if (!result->loadCurrentExecutable())
  561. throw MakeStringException(0, "Failed to create ILoadedDllEntry for current executable");
  562. return result.getClear();
  563. }
  564. extern DLLSERVER_API bool decompressResource(size32_t len, const void *data, MemoryBuffer &result)
  565. {
  566. bool hasVersion = len && (*(const byte *)data == 0x80);
  567. MemoryBuffer src;
  568. src.setBuffer(len, const_cast<void *>(data), false);
  569. byte version = 1;
  570. if (hasVersion)
  571. {
  572. src.skip(1);
  573. src.read(version);
  574. }
  575. switch (version)
  576. {
  577. case 1:
  578. decompressToBuffer(result, src);
  579. break;
  580. default:
  581. throwUnexpected();
  582. }
  583. return true;
  584. }
  585. extern DLLSERVER_API bool decompressResource(size32_t len, const void *data, StringBuffer &result)
  586. {
  587. MemoryBuffer tgt;
  588. if (len)
  589. decompressResource(len, data, tgt);
  590. tgt.append((char)0);
  591. unsigned expandedLen = tgt.length();
  592. result.setBuffer(expandedLen, reinterpret_cast<char *>(tgt.detach()), expandedLen-1);
  593. return true;
  594. }
  595. extern DLLSERVER_API void appendResource(MemoryBuffer & mb, size32_t len, const void *data, bool compress)
  596. {
  597. mb.append((byte)0x80).append(resourceHeaderVersion);
  598. if (compress)
  599. compressToBuffer(mb, len, data);
  600. else
  601. appendToBuffer(mb, len, data);
  602. }
  603. extern DLLSERVER_API void compressResource(MemoryBuffer & compressed, size32_t len, const void *data)
  604. {
  605. appendResource(compressed, len, data, true);
  606. }
  607. extern DLLSERVER_API bool getEmbeddedWorkUnitXML(ILoadedDllEntry *dll, StringBuffer &xml)
  608. {
  609. size32_t len = 0;
  610. const void * data = NULL;
  611. if (!dll->getResource(len, data, "WORKUNIT", 1000, false))
  612. return false;
  613. return decompressResource(len, data, xml);
  614. }
  615. extern DLLSERVER_API bool getEmbeddedWorkUnitBinary(ILoadedDllEntry *dll, MemoryBuffer &result)
  616. {
  617. size32_t len = 0;
  618. const void * data = NULL;
  619. if (!dll->getResource(len, data, "BINWORKUNIT", 1000, false))
  620. return false;
  621. return decompressResource(len, data, result);
  622. }
  623. extern DLLSERVER_API bool getEmbeddedManifestXML(const ILoadedDllEntry *dll, StringBuffer &xml)
  624. {
  625. size32_t len = 0;
  626. const void * data = NULL;
  627. if (!dll->getResource(len, data, "MANIFEST", 1000))
  628. return false;
  629. return decompressResource(len, data, xml);
  630. }
  631. extern DLLSERVER_API bool getEmbeddedArchiveXML(ILoadedDllEntry *dll, StringBuffer &xml)
  632. {
  633. size32_t len = 0;
  634. const void * data = NULL;
  635. if (!dll->getResource(len, data, "ARCHIVE", 1000))
  636. return false;
  637. return decompressResource(len, data, xml);
  638. }
  639. extern DLLSERVER_API IPropertyTree *getEmbeddedManifestPTree(const ILoadedDllEntry *dll)
  640. {
  641. StringBuffer xml;
  642. return getEmbeddedManifestXML(dll, xml) ? createPTreeFromXMLString(xml.str()) : createPTree();
  643. }
  644. extern DLLSERVER_API bool containsEmbeddedWorkUnit(ILoadedDllEntry *dll)
  645. {
  646. size32_t len = 0;
  647. const void * data = NULL;
  648. return dll->getResource(len, data, "BINWORKUNIT", 1000, false) ||
  649. dll->getResource(len, data, "WORKUNIT", 1000, false);
  650. }
  651. extern DLLSERVER_API bool getResourceXMLFromFile(const char *filename, const char *type, unsigned id, StringBuffer &xml)
  652. {
  653. MemoryBuffer data;
  654. if (!getResourceFromFile(filename, data, type, id))
  655. return false;
  656. return decompressResource(data.length(), data.toByteArray(), xml);
  657. }
  658. extern DLLSERVER_API bool getWorkunitXMLFromFile(const char *filename, StringBuffer &xml)
  659. {
  660. return getResourceXMLFromFile(filename, "WORKUNIT", 1000, xml);
  661. }
  662. extern DLLSERVER_API bool getArchiveXMLFromFile(const char *filename, StringBuffer &xml)
  663. {
  664. return getResourceXMLFromFile(filename, "ARCHIVE", 1000, xml);
  665. }
  666. extern DLLSERVER_API bool getManifestXMLFromFile(const char *filename, StringBuffer &xml)
  667. {
  668. return getResourceXMLFromFile(filename, "MANIFEST", 1000, xml);
  669. }
  670. extern DLLSERVER_API bool getWorkunitBinaryFromFile(const char *filename, MemoryBuffer &result)
  671. {
  672. MemoryBuffer data;
  673. if (!getResourceFromFile(filename, data, "BINWORKUNIT", 1000))
  674. return false;
  675. return decompressResource(data.length(), data.toByteArray(), result);
  676. }
  677. //-------------------------------------------------------------------------------------------------------------------
  678. extern DLLSERVER_API void getAdditionalPluginsPath(StringBuffer &pluginsPath, const char *_base)
  679. {
  680. // We only add the additional plugins if the plugins path already includes the default plugins location
  681. StringBuffer base(_base);
  682. removeTrailingPathSepChar(base);
  683. removeTrailingPathSepChar(pluginsPath);
  684. StringBuffer defaultLocation(base);
  685. defaultLocation.append(PATHSEPSTR "plugins");
  686. StringArray paths;
  687. paths.appendList(pluginsPath, ENVSEPSTR);
  688. #ifdef _CONTAINERIZED
  689. //MORE: No place to provide additional plugins...
  690. #else
  691. if (paths.contains(defaultLocation))
  692. {
  693. const char *additional = queryEnvironmentConf().queryProp("additionalPlugins");
  694. if (additional)
  695. {
  696. StringArray additionalPaths;
  697. additionalPaths.appendList(additional, ENVSEPSTR);
  698. ForEachItemIn(idx, additionalPaths)
  699. {
  700. const char *additionalPath = additionalPaths.item(idx);
  701. pluginsPath.append(ENVSEPCHAR);
  702. if (!isAbsolutePath(additionalPath))
  703. pluginsPath.append(base).append(PATHSEPSTR "versioned" PATHSEPSTR);
  704. pluginsPath.append(additionalPath);
  705. }
  706. }
  707. }
  708. #endif
  709. }
  710. bool SafePluginMap::addPlugin(const char *path, const char *dllname)
  711. {
  712. if (!endsWithIgnoreCase(path, SharedObjectExtension))
  713. {
  714. if (trace)
  715. DBGLOG("Ecl plugin %s ignored", path);
  716. return false;
  717. }
  718. try
  719. {
  720. CriticalBlock b(crit);
  721. ILoadedDllEntry *dll = map.getValue(dllname);
  722. if (!dll)
  723. {
  724. Owned<PluginDll> n = new PluginDll(path, NULL);
  725. // Note - we used to load plugins with global=true, but that caused issues when loading
  726. // Python3 and Python2 plugins at the same time as the export similar symbols
  727. // Loading with global=false should not cause any adverse issues
  728. if (!n->load(false, false) || !n->init(pluginCtx))
  729. throw MakeStringException(0, "Failed to load plugin %s", path);
  730. if (trace)
  731. n->logLoaded();
  732. map.setValue(dllname, n); // note: setValue links arg
  733. return true;
  734. }
  735. return false;
  736. }
  737. catch (IException * e) // MORE - not sure why we don't throw exceptions back here...
  738. {
  739. EXCLOG(e, "Loading plugin");
  740. e->Release();
  741. return false;
  742. }
  743. }
  744. ILoadedDllEntry * SafePluginMap::getPluginDll(const char *id, const char *version, bool checkVersion)
  745. {
  746. CriticalBlock b(crit);
  747. Linked<PluginDll> ret = static_cast<PluginDll *>(map.getValue(id));
  748. if (ret && checkVersion)
  749. {
  750. if (!ret->checkVersion(version))
  751. return NULL;
  752. }
  753. return ret.getLink();
  754. }
  755. void SafePluginMap::loadFromList(const char * pluginsList)
  756. {
  757. const char *pluginDir = pluginsList;
  758. for (;*pluginDir;)
  759. {
  760. StringBuffer thisPlugin;
  761. while (*pluginDir && *pluginDir != ENVSEPCHAR)
  762. thisPlugin.append(*pluginDir++);
  763. if(*pluginDir)
  764. pluginDir++;
  765. if(!thisPlugin.length())
  766. continue;
  767. Owned<IFile> file = createIFile(thisPlugin.str());
  768. if (file->isDirectory() == fileBool::foundYes)
  769. loadFromDirectory(thisPlugin);
  770. else
  771. {
  772. StringBuffer tail;
  773. splitFilename(thisPlugin, NULL, NULL, &tail, &tail);
  774. addPlugin(thisPlugin, tail.str());
  775. }
  776. }
  777. }
  778. bool SafePluginMap::loadNamed(const char * pluginDirectories, const char * plugin)
  779. {
  780. const char *pluginDir = pluginDirectories;
  781. for (;*pluginDir;)
  782. {
  783. StringBuffer thisFile;
  784. while (*pluginDir && *pluginDir != ENVSEPCHAR)
  785. thisFile.append(*pluginDir++);
  786. if(*pluginDir)
  787. pluginDir++;
  788. if(!thisFile.length())
  789. continue;
  790. Owned<IFile> dir = createIFile(thisFile);
  791. if (dir->isDirectory() == fileBool::foundYes)
  792. {
  793. Owned<IFile> file = createIFile(addPathSepChar(thisFile).append(plugin));
  794. if (file->exists())
  795. {
  796. if (addPlugin(thisFile, plugin))
  797. return true;
  798. }
  799. }
  800. else if (dir->isFile() == fileBool::foundYes)
  801. {
  802. StringBuffer tail;
  803. splitFilename(thisFile, NULL, NULL, &tail, &tail);
  804. if (streq(tail, plugin) && addPlugin(thisFile, plugin))
  805. return true;
  806. }
  807. }
  808. return false;
  809. }
  810. void SafePluginMap::loadFromDirectory(const char * pluginDirectory)
  811. {
  812. const char * mask = "*" SharedObjectExtension;
  813. Owned<IFile> pluginDir = createIFile(pluginDirectory);
  814. Owned<IDirectoryIterator> pluginFiles = pluginDir->directoryFiles(mask,false,false);
  815. ForEach(*pluginFiles)
  816. {
  817. const char *thisPlugin = pluginFiles->query().queryFilename();
  818. StringBuffer tail;
  819. splitFilename(thisPlugin, NULL, NULL, &tail, &tail);
  820. addPlugin(thisPlugin, tail.str());
  821. }
  822. }