ModuleSafeLoad.cpp 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139
  1. // Copyright (c) 2013-2017 Josh Blum
  2. // SPDX-License-Identifier: BSL-1.0
  3. #include <Pothos/System/Paths.hpp>
  4. #include <Pothos/Plugin/Module.hpp>
  5. #include <Pothos/Plugin/Exception.hpp>
  6. #include <Pothos/Util/FileLock.hpp>
  7. #include <Poco/Process.h>
  8. #include <Poco/Pipe.h>
  9. #include <Poco/File.h>
  10. #include <Poco/Path.h>
  11. #include <Poco/AutoPtr.h>
  12. #include <Poco/Util/PropertyFileConfiguration.h>
  13. #include <mutex>
  14. #include <cctype>
  15. //! The path used to cache the safe loads
  16. static std::string getModuleLoaderCachePath(void)
  17. {
  18. Poco::Path path(Pothos::System::getUserConfigPath());
  19. path.append("ModuleLoader.cache");
  20. return path.toString();
  21. }
  22. /***********************************************************************
  23. * named mutex for cache protection
  24. **********************************************************************/
  25. struct LoaderCacheFileLock : public Pothos::Util::FileLock
  26. {
  27. LoaderCacheFileLock(void):
  28. Pothos::Util::FileLock(getModuleLoaderCachePath()+".lock")
  29. {}
  30. };
  31. static Pothos::Util::FileLock &getLoaderFileLock(void)
  32. {
  33. static LoaderCacheFileLock lock;
  34. return lock;
  35. }
  36. static std::mutex &getLoaderMutex(void)
  37. {
  38. static std::mutex mutex;
  39. return mutex;
  40. }
  41. /***********************************************************************
  42. * calls to interface with file cache
  43. **********************************************************************/
  44. //! Get the string representation of a file's modification time
  45. static std::string getLastModifiedTimeStr(const std::string &path)
  46. {
  47. return std::to_string(Poco::File(path).getLastModified().epochMicroseconds());
  48. }
  49. //! Escape PropertyFile keys, problem with slashes
  50. static std::string escape(const std::string &in)
  51. {
  52. std::string out;
  53. for (const auto ch : in)
  54. {
  55. if (std::isalnum(ch)) out.push_back(ch);
  56. else out.push_back('_');
  57. }
  58. return out;
  59. }
  60. //! Was a previous safe-load of this module successful?
  61. static bool previousLoadWasSuccessful(const std::string &modulePath)
  62. {
  63. std::lock_guard<std::mutex> mutexLock(getLoaderMutex());
  64. std::lock_guard<Pothos::Util::FileLock> fileLock(getLoaderFileLock());
  65. Poco::AutoPtr<Poco::Util::PropertyFileConfiguration> cache(new Poco::Util::PropertyFileConfiguration());
  66. try {cache->load(getModuleLoaderCachePath());} catch(...){}
  67. try
  68. {
  69. auto libTime = getLastModifiedTimeStr(Pothos::System::getPothosRuntimeLibraryPath());
  70. auto modTime = getLastModifiedTimeStr(modulePath);
  71. auto cachedLibTime = cache->getString(escape(Pothos::System::getPothosRuntimeLibraryPath()));
  72. auto cachedModTime = cache->getString(escape(modulePath));
  73. return (libTime == cachedLibTime) and (modTime == cachedModTime);
  74. }
  75. catch (const Poco::NotFoundException &){}
  76. return false;
  77. }
  78. //! Mark that the safe load of this module was successful
  79. static void markCurrentLoadSuccessful(const std::string &modulePath)
  80. {
  81. std::lock_guard<std::mutex> mutexLock(getLoaderMutex());
  82. std::lock_guard<Pothos::Util::FileLock> fileLock(getLoaderFileLock());
  83. Poco::AutoPtr<Poco::Util::PropertyFileConfiguration> cache(new Poco::Util::PropertyFileConfiguration());
  84. try {cache->load(getModuleLoaderCachePath());} catch(...){}
  85. auto libTime = getLastModifiedTimeStr(Pothos::System::getPothosRuntimeLibraryPath());
  86. auto modTime = getLastModifiedTimeStr(modulePath);
  87. cache->setString(escape(Pothos::System::getPothosRuntimeLibraryPath()), libTime);
  88. cache->setString(escape(modulePath), modTime);
  89. try {cache->save(getModuleLoaderCachePath());} catch(...){}
  90. }
  91. /***********************************************************************
  92. * module safe load implementation
  93. **********************************************************************/
  94. Pothos::PluginModule Pothos::PluginModule::safeLoad(const std::string &path)
  95. {
  96. if (previousLoadWasSuccessful(path)) return PluginModule(path);
  97. const int success = 200;
  98. //create args
  99. Poco::Process::Args args;
  100. args.push_back("--load-module");
  101. args.push_back("\""+path+"\""); //add quotes for paths with spaces
  102. args.push_back("--success-code");
  103. args.push_back(std::to_string(success));
  104. //launch
  105. Poco::Pipe outPipe, errPipe;
  106. Poco::Process::Env env;
  107. Poco::ProcessHandle ph(Poco::Process::launch(
  108. Pothos::System::getPothosUtilExecutablePath(),
  109. args, nullptr, &outPipe, &errPipe, env));
  110. //close pipes to not overfill and backup
  111. outPipe.close();
  112. errPipe.close();
  113. //wait, check error condition, and throw
  114. if (ph.wait() != success)
  115. {
  116. throw Pothos::PluginModuleError("Pothos::PluginModule("+path+")", "failed safe load");
  117. }
  118. //it was safe, load into this process now
  119. markCurrentLoadSuccessful(path);
  120. return PluginModule(path);
  121. }