Audacity 3.2.0
module_linux.cpp
Go to the documentation of this file.
1//-----------------------------------------------------------------------------
2// Project : VST SDK
3//
4// Category : Helpers
5// Filename : public.sdk/source/vst/hosting/module_linux.cpp
6// Created by : Steinberg, 08/2016
7// Description : hosting module classes (linux implementation)
8//
9//-----------------------------------------------------------------------------
10// LICENSE
11// (c) 2021, Steinberg Media Technologies GmbH, All Rights Reserved
12//-----------------------------------------------------------------------------
13// Redistribution and use in source and binary forms, with or without modification,
14// are permitted provided that the following conditions are met:
15//
16// * Redistributions of source code must retain the above copyright notice,
17// this list of conditions and the following disclaimer.
18// * Redistributions in binary form must reproduce the above copyright notice,
19// this list of conditions and the following disclaimer in the documentation
20// and/or other materials provided with the distribution.
21// * Neither the name of the Steinberg Media Technologies nor the names of its
22// contributors may be used to endorse or promote products derived from this
23// software without specific prior written permission.
24//
25// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
26// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
27// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
28// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
29// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
30// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
31// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
32// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
33// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
34// OF THE POSSIBILITY OF SUCH DAMAGE.
35//-----------------------------------------------------------------------------
36
37#include "public.sdk/source/vst/hosting/module.h"
38#include "public.sdk/source/vst/utility/optional.h"
39#include "public.sdk/source/vst/utility/stringconvert.h"
40#include <algorithm>
41#include <dlfcn.h>
42#include <sys/types.h>
43#include <sys/utsname.h>
44#include <unistd.h>
45
46#if (__cplusplus >= 201707L)
47#if __has_include(<filesystem>)
48#define USE_EXPERIMENTAL_FS 0
49#elif __has_include(<experimental/filesystem>)
50#define USE_EXPERIMENTAL_FS 1
51#endif
52#else
53#define USE_EXPERIMENTAL_FS 1
54#endif
55
56#if USE_EXPERIMENTAL_FS == 1
57#include <experimental/filesystem>
58namespace filesystem = std::experimental::filesystem;
59#else
60#include <filesystem>
61namespace filesystem = std::filesystem;
62#endif
63
64//------------------------------------------------------------------------
65extern "C" {
66using ModuleEntryFunc = bool (PLUGIN_API*) (void*);
67using ModuleExitFunc = bool (PLUGIN_API*) ();
68}
69
70//------------------------------------------------------------------------
71namespace VST3 {
72namespace Hosting {
73
74using Path = filesystem::path;
75
76//------------------------------------------------------------------------
77namespace {
78
79//------------------------------------------------------------------------
80Optional<std::string> getCurrentMachineName ()
81{
82 struct utsname unameData;
83
84 int res = uname (&unameData);
85 if (res != 0)
86 return {};
87
88 return {unameData.machine};
89}
90
91//------------------------------------------------------------------------
92Optional<Path> getApplicationPath ()
93{
94 std::string appPath = "";
95
96 pid_t pid = getpid ();
97 char buf[10];
98 sprintf (buf, "%d", pid);
99 std::string _link = "/proc/";
100 _link.append (buf);
101 _link.append ("/exe");
102 char proc[1024];
103 int ch = readlink (_link.c_str (), proc, 1024);
104 if (ch == -1)
105 return {};
106
107 proc[ch] = 0;
108 appPath = proc;
109 std::string::size_type t = appPath.find_last_of ("/");
110 appPath = appPath.substr (0, t);
111
112 return Path {appPath};
113}
114
115//------------------------------------------------------------------------
116class LinuxModule : public Module
117{
118public:
119 template <typename T>
120 T getFunctionPointer (const char* name)
121 {
122 return reinterpret_cast<T> (dlsym (mModule, name));
123 }
124
125 ~LinuxModule () override
126 {
127 factory = PluginFactory (nullptr);
128
129 if (mModule)
130 {
131 if (auto moduleExit = getFunctionPointer<ModuleExitFunc> ("ModuleExit"))
132 moduleExit ();
133
134 dlclose (mModule);
135 }
136 }
137
138 static Optional<Path> getSOPath (const std::string& inPath)
139 {
140 Path modulePath {inPath};
141 if (!filesystem::is_directory (modulePath))
142 return {};
143
144 auto stem = modulePath.stem ();
145
146 modulePath /= "Contents";
147 if (!filesystem::is_directory (modulePath))
148 return {};
149
150 // use the Machine Hardware Name (from uname cmd-line) as prefix for "-linux"
151 auto machine = getCurrentMachineName ();
152 if (!machine)
153 return {};
154
155 modulePath /= *machine + "-linux";
156 if (!filesystem::is_directory (modulePath))
157 return {};
158
159 stem.replace_extension (".so");
160 modulePath /= stem;
161 return Optional<Path> (std::move (modulePath));
162 }
163
164 bool load (const std::string& inPath, std::string& errorDescription) override
165 {
166 auto modulePath = getSOPath (inPath);
167 if (!modulePath)
168 {
169 errorDescription = inPath + " is not a module directory.";
170 return false;
171 }
172
173 mModule = dlopen (reinterpret_cast<const char*> (modulePath->generic_string ().data ()),
174 RTLD_LAZY);
175 if (!mModule)
176 {
177 errorDescription = "dlopen failed.\n";
178 errorDescription += dlerror ();
179 return false;
180 }
181 // ModuleEntry is mandatory
182 auto moduleEntry = getFunctionPointer<ModuleEntryFunc> ("ModuleEntry");
183 if (!moduleEntry)
184 {
185 errorDescription =
186 "The shared library does not export the required 'ModuleEntry' function";
187 return false;
188 }
189 // ModuleExit is mandatory
190 auto moduleExit = getFunctionPointer<ModuleExitFunc> ("ModuleExit");
191 if (!moduleExit)
192 {
193 errorDescription =
194 "The shared library does not export the required 'ModuleExit' function";
195 return false;
196 }
197 auto factoryProc = getFunctionPointer<GetFactoryProc> ("GetPluginFactory");
198 if (!factoryProc)
199 {
200 errorDescription =
201 "The shared library does not export the required 'GetPluginFactory' function";
202 return false;
203 }
204
205 if (!moduleEntry (mModule))
206 {
207 errorDescription = "Calling 'ModuleEntry' failed";
208 return false;
209 }
210 auto f = Steinberg::FUnknownPtr<Steinberg::IPluginFactory> (owned (factoryProc ()));
211 if (!f)
212 {
213 errorDescription = "Calling 'GetPluginFactory' returned nullptr";
214 return false;
215 }
216 factory = PluginFactory (f);
217 return true;
218 }
219
220 void* mModule {nullptr};
221};
222
223//------------------------------------------------------------------------
224void findFilesWithExt (const std::string& path, const std::string& ext, Module::PathList& pathList,
225 bool recursive = true)
226{
227 try
228 {
229 for (auto& p : filesystem::directory_iterator (path))
230 {
231 if (p.path ().extension () == ext)
232 {
233 pathList.push_back (p.path ().generic_string ());
234 }
235 else if (recursive && p.status ().type () == filesystem::file_type::directory)
236 {
237 findFilesWithExt (p.path (), ext, pathList);
238 }
239 }
240 }
241 catch (...)
242 {
243 }
244}
245
246//------------------------------------------------------------------------
247void findModules (const std::string& path, Module::PathList& pathList)
248{
249 findFilesWithExt (path, ".vst3", pathList);
250}
251
252//------------------------------------------------------------------------
253} // anonymous
254
255//------------------------------------------------------------------------
256Module::Ptr Module::create (const std::string& path, std::string& errorDescription)
257{
258 auto _module = std::make_shared<LinuxModule> ();
259 if (_module->load (path, errorDescription))
260 {
261 auto it = std::find_if (path.rbegin (), path.rend (),
262 [] (const std::string::value_type& c) { return c == '/'; });
263 _module->path = path;
264 if (it != path.rend ())
265 _module->name = {it.base (), path.end ()};
266 return _module;
267 }
268 return nullptr;
269}
270
271//------------------------------------------------------------------------
272Module::PathList Module::getModulePaths ()
273{
274 /* VST3 component locations on linux :
275 * User privately installed : $HOME/.vst3/
276 * Distribution installed : /usr/lib/vst3/
277 * Locally installed : /usr/local/lib/vst3/
278 * Application : /$APPFOLDER/vst3/
279 */
280
281 const auto systemPaths = {"/usr/lib/vst3/", "/usr/local/lib/vst3/"};
282
283 PathList list;
284 if (auto homeDir = getenv ("HOME"))
285 {
286 filesystem::path homePath (homeDir);
287 homePath /= ".vst3";
288 findModules (homePath.generic_string (), list);
289 }
290 for (auto path : systemPaths)
291 findModules (path, list);
292
293 // application level
294 auto appPath = getApplicationPath ();
295 if (appPath)
296 {
297 *appPath /= "vst3";
298 findModules (appPath->generic_string (), list);
299 }
300
301 return list;
302}
303
304//------------------------------------------------------------------------
305Module::SnapshotList Module::getSnapshots (const std::string& modulePath)
306{
307 SnapshotList result;
308 filesystem::path path (modulePath);
309 path /= "Contents";
310 path /= "Resources";
311 path /= "Snapshots";
312 PathList pngList;
313 findFilesWithExt (path, ".png", pngList, false);
314 for (auto& png : pngList)
315 {
316 filesystem::path p (png);
317 auto filename = p.filename ().generic_string ();
318 auto uid = Snapshot::decodeUID (filename);
319 if (!uid)
320 continue;
321 auto scaleFactor = 1.;
322 if (auto decodedScaleFactor = Snapshot::decodeScaleFactor (filename))
323 scaleFactor = *decodedScaleFactor;
324
325 Module::Snapshot::ImageDesc desc;
326 desc.scaleFactor = scaleFactor;
327 desc.path = std::move (png);
328 bool found = false;
329 for (auto& entry : result)
330 {
331 if (entry.uid != *uid)
332 continue;
333 found = true;
334 entry.images.emplace_back (std::move (desc));
335 break;
336 }
337 if (found)
338 continue;
339 Module::Snapshot snapshot;
340 snapshot.uid = *uid;
341 snapshot.images.emplace_back (std::move (desc));
342 result.emplace_back (std::move (snapshot));
343 }
344 return result;
345}
346
347//------------------------------------------------------------------------
348} // Hosting
349} // VST3
static RegisteredToolbarFactory factory
const TranslatableString name
Definition: Distortion.cpp:76
static ProjectFileIORegistry::AttributeWriterEntry entry
static Optional< Path > getSOPath(const std::string &inPath)
bool load(const std::string &inPath, std::string &errorDescription) override
bool(PLUGIN_API *)() ModuleExitFunc
bool(PLUGIN_API *)(void *) ModuleEntryFunc
void findModules(const std::string &path, Module::PathList &pathList)
void findFilesWithExt(const std::string &path, const std::string &ext, Module::PathList &pathList, bool recursive=true)
filesystem::path Path
const TranslatableString desc
Definition: ExportPCM.cpp:51