You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

507 lines
13KB

  1. #include <arch.hpp>
  2. #include <thread>
  3. #include <map>
  4. #include <stdexcept>
  5. #include <tuple>
  6. #include <sys/types.h>
  7. #include <sys/stat.h>
  8. #include <unistd.h>
  9. #include <sys/param.h> // for MAXPATHLEN
  10. #include <fcntl.h>
  11. #if defined ARCH_WIN
  12. #include <windows.h>
  13. #include <direct.h>
  14. #else
  15. #include <dlfcn.h> // for dlopen
  16. #endif
  17. #include <dirent.h>
  18. #include <osdialog.h>
  19. #include <jansson.h>
  20. #include <plugin.hpp>
  21. #include <system.hpp>
  22. #include <asset.hpp>
  23. #include <string.hpp>
  24. #include <context.hpp>
  25. #include <plugin/callbacks.hpp>
  26. #include <settings.hpp>
  27. namespace rack {
  28. namespace core {
  29. void init(rack::plugin::Plugin* plugin);
  30. } // namespace core
  31. namespace plugin {
  32. ////////////////////
  33. // private API
  34. ////////////////////
  35. static void* getSymbol(void* handle, const char* name) {
  36. if (!handle)
  37. return NULL;
  38. #if defined ARCH_WIN
  39. return (void*) GetProcAddress((HMODULE) handle, name);
  40. #else
  41. return dlsym(handle, name);
  42. #endif
  43. }
  44. /** Returns library handle */
  45. static void* loadLibrary(std::string libraryPath) {
  46. #if defined ARCH_WIN
  47. SetErrorMode(SEM_NOOPENFILEERRORBOX | SEM_FAILCRITICALERRORS);
  48. std::wstring libraryFilenameW = string::UTF8toUTF16(libraryPath);
  49. HINSTANCE handle = LoadLibraryW(libraryFilenameW.c_str());
  50. SetErrorMode(0);
  51. if (!handle) {
  52. int error = GetLastError();
  53. throw Exception("Failed to load library %s: code %d", libraryPath.c_str(), error);
  54. }
  55. #else
  56. // Since Rack 2, plugins on Linux/Mac link to the absolute path /tmp/Rack2/libRack.<ext>
  57. // Create a symlink at /tmp/Rack2 to the system dir containting libRack.
  58. std::string systemDir = system::getAbsolute(asset::systemDir);
  59. std::string linkPath = "/tmp/Rack2";
  60. if (!settings::devMode) {
  61. // Clean up old symbolic link in case a different edition was run earlier
  62. system::remove(linkPath);
  63. system::createSymbolicLink(systemDir, linkPath);
  64. }
  65. // Load library with dlopen
  66. void* handle = NULL;
  67. #if defined ARCH_LIN
  68. handle = dlopen(libraryPath.c_str(), RTLD_NOW | RTLD_LOCAL);
  69. #elif defined ARCH_MAC
  70. handle = dlopen(libraryPath.c_str(), RTLD_NOW | RTLD_LOCAL);
  71. #endif
  72. if (!settings::devMode) {
  73. system::remove(linkPath);
  74. }
  75. if (!handle)
  76. throw Exception("Failed to load library %s: %s", libraryPath.c_str(), dlerror());
  77. #endif
  78. return handle;
  79. }
  80. typedef void (*InitCallback)(Plugin*);
  81. static InitCallback loadPluginCallback(Plugin* plugin) {
  82. // Load plugin library
  83. std::string libraryExt;
  84. #if defined ARCH_LIN
  85. libraryExt = "so";
  86. #elif defined ARCH_WIN
  87. libraryExt = "dll";
  88. #elif defined ARCH_MAC
  89. libraryExt = "dylib";
  90. #endif
  91. std::string libraryFilename = "plugin." + libraryExt;
  92. std::string libraryPath = system::join(plugin->path, libraryFilename);
  93. // Check file existence
  94. if (!system::isFile(libraryPath))
  95. throw Exception("Plugin binary not found at %s", libraryPath.c_str());
  96. // Load dynamic/shared library
  97. plugin->handle = loadLibrary(libraryPath);
  98. // Get plugin's init() function
  99. InitCallback initCallback = (InitCallback) getSymbol(plugin->handle, "init");
  100. if (!initCallback)
  101. throw Exception("Failed to read init() symbol in %s", libraryPath.c_str());
  102. return initCallback;
  103. }
  104. /** If path is blank, loads Core */
  105. static Plugin* loadPlugin(std::string path) {
  106. if (path == "")
  107. INFO("Loading Core plugin");
  108. else
  109. INFO("Loading plugin from %s", path.c_str());
  110. Plugin* plugin = new Plugin;
  111. try {
  112. // Set plugin path
  113. plugin->path = (path == "") ? asset::systemDir : path;
  114. // Get modified timestamp
  115. if (path != "") {
  116. struct stat statbuf;
  117. if (!stat(path.c_str(), &statbuf)) {
  118. #if defined ARCH_MAC
  119. plugin->modifiedTimestamp = (double) statbuf.st_mtimespec.tv_sec + statbuf.st_mtimespec.tv_nsec * 1e-9;
  120. #elif defined ARCH_WIN
  121. plugin->modifiedTimestamp = (double) statbuf.st_mtime;
  122. #elif defined ARCH_LIN
  123. plugin->modifiedTimestamp = (double) statbuf.st_mtim.tv_sec + statbuf.st_mtim.tv_nsec * 1e-9;
  124. #endif
  125. }
  126. }
  127. // Load plugin.json
  128. std::string manifestFilename = (path == "") ? asset::system("Core.json") : system::join(path, "plugin.json");
  129. FILE* file = std::fopen(manifestFilename.c_str(), "r");
  130. if (!file)
  131. throw Exception("Manifest file %s does not exist", manifestFilename.c_str());
  132. DEFER({std::fclose(file);});
  133. json_error_t error;
  134. json_t* rootJ = json_loadf(file, 0, &error);
  135. if (!rootJ)
  136. throw Exception("JSON parsing error at %s %d:%d %s", manifestFilename.c_str(), error.line, error.column, error.text);
  137. DEFER({json_decref(rootJ);});
  138. // Load manifest
  139. plugin->fromJson(rootJ);
  140. // Reject plugin if slug already exists
  141. Plugin* existingPlugin = getPlugin(plugin->slug);
  142. if (existingPlugin)
  143. throw Exception("Plugin %s is already loaded, not attempting to load it again", plugin->slug.c_str());
  144. // Call init callback
  145. InitCallback initCallback;
  146. if (path == "") {
  147. initCallback = core::init;
  148. }
  149. else {
  150. initCallback = loadPluginCallback(plugin);
  151. }
  152. initCallback(plugin);
  153. // Load modules manifest
  154. json_t* modulesJ = json_object_get(rootJ, "modules");
  155. plugin->modulesFromJson(modulesJ);
  156. // Call settingsFromJson() if exists
  157. // Returns NULL for Core.
  158. auto settingsFromJson = (decltype(&::settingsFromJson)) getSymbol(plugin->handle, "settingsFromJson");
  159. if (settingsFromJson) {
  160. json_t* settingsJ = json_object_get(settings::pluginSettingsJ, plugin->slug.c_str());
  161. if (settingsJ)
  162. settingsFromJson(settingsJ);
  163. }
  164. }
  165. catch (Exception& e) {
  166. WARN("Could not load plugin %s: %s", path.c_str(), e.what());
  167. delete plugin;
  168. return NULL;
  169. }
  170. INFO("Loaded plugin %s %s", plugin->slug.c_str(), plugin->version.c_str());
  171. plugins.push_back(plugin);
  172. return plugin;
  173. }
  174. static void loadPlugins(std::string path) {
  175. for (std::string pluginPath : system::getEntries(path)) {
  176. if (!system::isDirectory(pluginPath))
  177. continue;
  178. if (!loadPlugin(pluginPath)) {
  179. // Ignore bad plugins. They are reported in the log.
  180. }
  181. }
  182. }
  183. static void extractPackages(std::string path) {
  184. std::string message;
  185. for (std::string packagePath : system::getEntries(path)) {
  186. if (!system::isFile(packagePath))
  187. continue;
  188. if (system::getExtension(packagePath) != ".vcvplugin")
  189. continue;
  190. // Extract package
  191. INFO("Extracting package %s", packagePath.c_str());
  192. try {
  193. system::unarchiveToDirectory(packagePath, path);
  194. }
  195. catch (Exception& e) {
  196. WARN("Plugin package %s failed to extract: %s", packagePath.c_str(), e.what());
  197. message += string::f("Could not extract plugin package %s\n", packagePath.c_str());
  198. continue;
  199. }
  200. // Remove package
  201. system::remove(packagePath.c_str());
  202. }
  203. if (!message.empty()) {
  204. osdialog_message(OSDIALOG_WARNING, OSDIALOG_OK, message.c_str());
  205. }
  206. }
  207. ////////////////////
  208. // public API
  209. ////////////////////
  210. void init() {
  211. // Don't re-initialize
  212. assert(plugins.empty());
  213. // Load Core
  214. loadPlugin("");
  215. // Get user plugins directory
  216. if (settings::devMode) {
  217. pluginsPath = asset::user("plugins");
  218. }
  219. else {
  220. pluginsPath = asset::user("plugins-" + APP_OS + "-" + APP_CPU);
  221. }
  222. // In Rack <2.4.0, plugins dir was "plugins" regardless of arch.
  223. // Rename old dir if running x64.
  224. #if defined ARCH_X64
  225. std::string oldPluginsPath = asset::user("plugins");
  226. if (system::isDirectory(oldPluginsPath)) {
  227. system::rename(oldPluginsPath, pluginsPath);
  228. }
  229. #endif
  230. system::createDirectory(pluginsPath);
  231. // Don't load plugins if safe mode is enabled
  232. if (settings::safeMode)
  233. return;
  234. // Extract packages and load plugins
  235. extractPackages(pluginsPath);
  236. loadPlugins(pluginsPath);
  237. // If Fundamental wasn't loaded, copy the bundled Fundamental package and load it
  238. if (!settings::devMode && !getPlugin("Fundamental")) {
  239. std::string fundamentalPackage = get(system::glob(asset::system("Fundamental-*-" + APP_OS + "-" + APP_CPU + ".vcvplugin")), 0);
  240. std::string fundamentalDir = system::join(pluginsPath, "Fundamental");
  241. if (fundamentalPackage != "" && system::isFile(fundamentalPackage)) {
  242. INFO("Extracting bundled Fundamental package");
  243. try {
  244. system::unarchiveToDirectory(fundamentalPackage.c_str(), pluginsPath.c_str());
  245. loadPlugin(fundamentalDir);
  246. }
  247. catch (Exception& e) {
  248. WARN("Could not extract Fundamental package: %s", e.what());
  249. }
  250. }
  251. }
  252. }
  253. static void destroyPlugin(Plugin* plugin) {
  254. void* handle = plugin->handle;
  255. // Call destroy() if defined in the plugin library
  256. typedef void (*DestroyCallback)();
  257. DestroyCallback destroyCallback = NULL;
  258. if (handle) {
  259. destroyCallback = (DestroyCallback) getSymbol(handle, "destroy");
  260. }
  261. if (destroyCallback) {
  262. try {
  263. destroyCallback();
  264. }
  265. catch (Exception& e) {
  266. WARN("Could not destroy plugin %s", plugin->slug.c_str());
  267. }
  268. }
  269. // We must delete the Plugin instance *before* freeing the library, because the vtables of Model subclasses are defined in the library, which are needed in the Plugin destructor.
  270. delete plugin;
  271. // Free library handle
  272. if (handle) {
  273. #if defined ARCH_WIN
  274. FreeLibrary((HINSTANCE) handle);
  275. #else
  276. dlclose(handle);
  277. #endif
  278. }
  279. }
  280. void destroy() {
  281. while (!plugins.empty()) {
  282. Plugin* plugin = plugins.back();
  283. INFO("Destroying plugin %s", plugin->name.c_str());
  284. destroyPlugin(plugin);
  285. plugins.pop_back();
  286. }
  287. assert(plugins.empty());
  288. }
  289. void settingsMergeJson(json_t* rootJ) {
  290. for (Plugin* plugin : plugins) {
  291. auto settingsToJson = (decltype(&::settingsToJson)) getSymbol(plugin->handle, "settingsToJson");
  292. if (settingsToJson) {
  293. json_t* settingsJ = settingsToJson();
  294. json_object_set_new(rootJ, plugin->slug.c_str(), settingsJ);
  295. }
  296. else {
  297. json_object_del(rootJ, plugin->slug.c_str());
  298. }
  299. }
  300. }
  301. /** Given slug => fallback slug.
  302. Supports bidirectional fallbacks.
  303. To request fallback slugs to be added to this list, contact VCV support.
  304. */
  305. static const std::map<std::string, std::string> pluginSlugFallbacks = {
  306. {"VultModulesFree", "VultModules"},
  307. {"VultModules", "VultModulesFree"},
  308. {"AudibleInstrumentsPreview", "AudibleInstruments"},
  309. {"SequelSequencers", "DanielDavies"},
  310. {"DelexanderVol1", "DelexandraVol1"},
  311. {"VCV-Pro", "Fundamental"},
  312. // {"", ""},
  313. };
  314. Plugin* getPlugin(const std::string& pluginSlug) {
  315. if (pluginSlug.empty())
  316. return NULL;
  317. auto it = std::find_if(plugins.begin(), plugins.end(), [=](Plugin* p) {
  318. return p->slug == pluginSlug;
  319. });
  320. if (it != plugins.end())
  321. return *it;
  322. return NULL;
  323. }
  324. Plugin* getPluginFallback(const std::string& pluginSlug) {
  325. if (pluginSlug.empty())
  326. return NULL;
  327. // Attempt example plugin
  328. Plugin* p = getPlugin(pluginSlug);
  329. if (p)
  330. return p;
  331. // Attempt fallback plugin slug
  332. auto it = pluginSlugFallbacks.find(pluginSlug);
  333. if (it != pluginSlugFallbacks.end())
  334. return getPlugin(it->second);
  335. return NULL;
  336. }
  337. /** Given slug => fallback slug.
  338. Correctly handles bidirectional fallbacks.
  339. To request fallback slugs to be added to this list, open a GitHub issue.
  340. */
  341. using PluginModuleSlug = std::tuple<std::string, std::string>;
  342. static const std::map<PluginModuleSlug, PluginModuleSlug> moduleSlugFallbacks = {
  343. {{"MindMeld-ShapeMasterPro", "ShapeMasterPro"}, {"MindMeldModular", "ShapeMaster"}},
  344. {{"MindMeldModular", "ShapeMaster"}, {"MindMeld-ShapeMasterPro", "ShapeMasterPro"}},
  345. // {{"", ""}, {"", ""}},
  346. };
  347. Model* getModel(const std::string& pluginSlug, const std::string& modelSlug) {
  348. if (pluginSlug.empty() || modelSlug.empty())
  349. return NULL;
  350. Plugin* p = getPlugin(pluginSlug);
  351. if (!p)
  352. return NULL;
  353. return p->getModel(modelSlug);
  354. }
  355. Model* getModelFallback(const std::string& pluginSlug, const std::string& modelSlug) {
  356. if (pluginSlug.empty() || modelSlug.empty())
  357. return NULL;
  358. // Attempt exact plugin and model
  359. Model* m = getModel(pluginSlug, modelSlug);
  360. if (m)
  361. return m;
  362. // Attempt fallback module
  363. auto it = moduleSlugFallbacks.find(std::make_tuple(pluginSlug, modelSlug));
  364. if (it != moduleSlugFallbacks.end()) {
  365. Model* m = getModel(std::get<0>(it->second), std::get<1>(it->second));
  366. if (m)
  367. return m;
  368. }
  369. // Attempt fallback plugin
  370. auto it2 = pluginSlugFallbacks.find(pluginSlug);
  371. if (it2 != pluginSlugFallbacks.end()) {
  372. Model* m = getModel(it2->second, modelSlug);
  373. if (m)
  374. return m;
  375. }
  376. return NULL;
  377. }
  378. Model* modelFromJson(json_t* moduleJ) {
  379. // Get slugs
  380. json_t* pluginSlugJ = json_object_get(moduleJ, "plugin");
  381. if (!pluginSlugJ)
  382. throw Exception("\"plugin\" property not found in module JSON");
  383. std::string pluginSlug = json_string_value(pluginSlugJ);
  384. pluginSlug = normalizeSlug(pluginSlug);
  385. json_t* modelSlugJ = json_object_get(moduleJ, "model");
  386. if (!modelSlugJ)
  387. throw Exception("\"model\" property not found in module JSON");
  388. std::string modelSlug = json_string_value(modelSlugJ);
  389. modelSlug = normalizeSlug(modelSlug);
  390. // Get Model
  391. Model* model = getModelFallback(pluginSlug, modelSlug);
  392. if (!model)
  393. throw Exception("Could not find module %s/%s", pluginSlug.c_str(), modelSlug.c_str());
  394. return model;
  395. }
  396. bool isSlugValid(const std::string& slug) {
  397. for (char c : slug) {
  398. if (!(std::isalnum(c) || c == '-' || c == '_'))
  399. return false;
  400. }
  401. return true;
  402. }
  403. std::string normalizeSlug(const std::string& slug) {
  404. std::string s;
  405. for (char c : slug) {
  406. if (!(std::isalnum(c) || c == '-' || c == '_'))
  407. continue;
  408. s += c;
  409. }
  410. return s;
  411. }
  412. std::string pluginsPath;
  413. std::vector<Plugin*> plugins;
  414. } // namespace plugin
  415. } // namespace rack