Chris@2
|
1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
|
Chris@0
|
2
|
Chris@0
|
3 /**
|
Chris@28
|
4 * [Vamp] Plugin Load Checker
|
Chris@0
|
5 *
|
Chris@4
|
6 * This program accepts the name of a descriptor symbol as its only
|
Chris@1
|
7 * command-line argument. It then reads a list of plugin library paths
|
Chris@1
|
8 * from stdin, one per line. For each path read, it attempts to load
|
Chris@4
|
9 * that library and retrieve the named descriptor symbol, printing a
|
Chris@1
|
10 * line to stdout reporting whether this was successful or not and
|
Chris@1
|
11 * then flushing stdout. The output line format is described
|
Chris@1
|
12 * below. The program exits with code 0 if all libraries were loaded
|
Chris@1
|
13 * successfully and non-zero otherwise.
|
Chris@0
|
14 *
|
Chris@0
|
15 * Note that library paths must be ready to pass to dlopen() or
|
Chris@0
|
16 * equivalent; this usually means they should be absolute paths.
|
Chris@0
|
17 *
|
Chris@0
|
18 * Output line for successful load of library libname.so:
|
Chris@0
|
19 * SUCCESS|/path/to/libname.so|
|
Chris@0
|
20 *
|
Chris@0
|
21 * Output line for failed load of library libname.so:
|
Chris@0
|
22 * FAILURE|/path/to/libname.so|Reason for failure if available
|
Chris@0
|
23 *
|
Chris@28
|
24 * Although this program was written for use with Vamp audio analysis
|
Chris@28
|
25 * plugins, it also works with other plugin formats. The program has
|
Chris@28
|
26 * some hardcoded knowledge of Vamp, LADSPA, and DSSI plugins, but it
|
Chris@28
|
27 * can be used with any plugins that involve loading DLLs and looking
|
Chris@28
|
28 * up descriptor functions from them.
|
Chris@28
|
29 *
|
Chris@2
|
30 * Sometimes plugins will crash completely on load, bringing down this
|
Chris@2
|
31 * program with them. If the program exits before all listed plugins
|
Chris@2
|
32 * have been checked, this means that the plugin following the last
|
Chris@2
|
33 * reported one has crashed. Typically the caller may want to run it
|
Chris@2
|
34 * again, omitting that plugin.
|
Chris@0
|
35 */
|
Chris@0
|
36
|
Chris@5
|
37 /*
|
Chris@28
|
38 Copyright (c) 2016-2017 Queen Mary, University of London
|
Chris@5
|
39
|
Chris@5
|
40 Permission is hereby granted, free of charge, to any person
|
Chris@5
|
41 obtaining a copy of this software and associated documentation
|
Chris@5
|
42 files (the "Software"), to deal in the Software without
|
Chris@5
|
43 restriction, including without limitation the rights to use, copy,
|
Chris@5
|
44 modify, merge, publish, distribute, sublicense, and/or sell copies
|
Chris@5
|
45 of the Software, and to permit persons to whom the Software is
|
Chris@5
|
46 furnished to do so, subject to the following conditions:
|
Chris@5
|
47
|
Chris@5
|
48 The above copyright notice and this permission notice shall be
|
Chris@5
|
49 included in all copies or substantial portions of the Software.
|
Chris@5
|
50
|
Chris@5
|
51 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
Chris@5
|
52 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
Chris@5
|
53 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
Chris@5
|
54 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
|
Chris@5
|
55 CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
|
Chris@5
|
56 CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
Chris@5
|
57 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
Chris@5
|
58
|
Chris@5
|
59 Except as contained in this notice, the names of the Centre for
|
Chris@5
|
60 Digital Music and Queen Mary, University of London shall not be
|
Chris@5
|
61 used in advertising or otherwise to promote the sale, use or other
|
Chris@5
|
62 dealings in this Software without prior written authorization.
|
Chris@5
|
63 */
|
Chris@5
|
64
|
Chris@27
|
65 #include "../version.h"
|
Chris@27
|
66
|
Chris@28
|
67 static const char programName[] = "vamp-plugin-load-checker";
|
Chris@28
|
68
|
Chris@0
|
69 #ifdef _WIN32
|
Chris@0
|
70 #include <windows.h>
|
Chris@0
|
71 #include <process.h>
|
Chris@10
|
72 #include <string>
|
Chris@10
|
73 #ifdef UNICODE
|
Chris@18
|
74 static std::string lastLibraryName = "";
|
Chris@10
|
75 static HMODULE LoadLibraryUTF8(std::string name) {
|
Chris@18
|
76 lastLibraryName = name;
|
Chris@10
|
77 int n = name.size();
|
Chris@16
|
78 int wn = MultiByteToWideChar(CP_UTF8, 0, name.c_str(), n, 0, 0);
|
Chris@16
|
79 wchar_t *wname = new wchar_t[wn+1];
|
Chris@16
|
80 wn = MultiByteToWideChar(CP_UTF8, 0, name.c_str(), n, wname, wn);
|
Chris@16
|
81 wname[wn] = L'\0';
|
Chris@10
|
82 HMODULE h = LoadLibraryW(wname);
|
Chris@10
|
83 delete[] wname;
|
Chris@10
|
84 return h;
|
Chris@10
|
85 }
|
Chris@10
|
86 static std::string GetErrorText() {
|
Chris@10
|
87 wchar_t *buffer;
|
Chris@10
|
88 DWORD err = GetLastError();
|
Chris@10
|
89 FormatMessage(
|
Chris@10
|
90 FORMAT_MESSAGE_ALLOCATE_BUFFER |
|
Chris@10
|
91 FORMAT_MESSAGE_FROM_SYSTEM |
|
Chris@10
|
92 FORMAT_MESSAGE_IGNORE_INSERTS,
|
Chris@10
|
93 NULL,
|
Chris@10
|
94 err,
|
Chris@10
|
95 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
Chris@10
|
96 (LPTSTR) &buffer,
|
Chris@10
|
97 0, NULL );
|
Chris@16
|
98 int wn = wcslen(buffer);
|
Chris@16
|
99 int n = WideCharToMultiByte(CP_UTF8, 0, buffer, wn, 0, 0, 0, 0);
|
Chris@17
|
100 if (n < 0) {
|
Chris@17
|
101 LocalFree(&buffer);
|
Chris@17
|
102 return "Unable to convert error string (internal error)";
|
Chris@17
|
103 }
|
Chris@16
|
104 char *text = new char[n+1];
|
Chris@17
|
105 (void)WideCharToMultiByte(CP_UTF8, 0, buffer, wn, text, n, 0, 0);
|
Chris@16
|
106 text[n] = '\0';
|
Chris@10
|
107 std::string s(text);
|
Chris@10
|
108 LocalFree(&buffer);
|
Chris@10
|
109 delete[] text;
|
Chris@10
|
110 for (int i = s.size(); i > 0; ) {
|
Chris@10
|
111 --i;
|
Chris@10
|
112 if (s[i] == '\n' || s[i] == '\r') {
|
Chris@10
|
113 s.erase(i, 1);
|
Chris@10
|
114 }
|
Chris@10
|
115 }
|
Chris@18
|
116 std::size_t pos = s.find("%1");
|
Chris@18
|
117 if (pos != std::string::npos && lastLibraryName != "") {
|
Chris@18
|
118 s.replace(pos, 2, lastLibraryName);
|
Chris@18
|
119 }
|
Chris@10
|
120 return s;
|
Chris@10
|
121 }
|
Chris@10
|
122 #define DLOPEN(a,b) LoadLibraryUTF8(a)
|
Chris@10
|
123 #else
|
Chris@10
|
124 #define DLOPEN(a,b) LoadLibrary((a).c_str())
|
Chris@10
|
125 #define GetErrorText() ""
|
Chris@10
|
126 #endif
|
Chris@10
|
127 #define DLSYM(a,b) (void *)GetProcAddress((HINSTANCE)(a),(b).c_str())
|
Chris@0
|
128 #define DLCLOSE(a) (!FreeLibrary((HINSTANCE)(a)))
|
Chris@10
|
129 #define DLERROR() (GetErrorText())
|
Chris@0
|
130 #else
|
Chris@0
|
131 #include <dlfcn.h>
|
Chris@0
|
132 #define DLOPEN(a,b) dlopen((a).c_str(),(b))
|
Chris@0
|
133 #define DLSYM(a,b) dlsym((a),(b).c_str())
|
Chris@0
|
134 #define DLCLOSE(a) dlclose((a))
|
Chris@0
|
135 #define DLERROR() dlerror()
|
Chris@0
|
136 #endif
|
Chris@0
|
137
|
Chris@0
|
138 #include <string>
|
Chris@0
|
139 #include <iostream>
|
Chris@0
|
140
|
Chris@12
|
141 //#include <unistd.h>
|
Chris@6
|
142
|
Chris@0
|
143 using namespace std;
|
Chris@0
|
144
|
Chris@0
|
145 string error()
|
Chris@0
|
146 {
|
Chris@10
|
147 string e = DLERROR();
|
Chris@0
|
148 if (e == "") return "(unknown error)";
|
Chris@0
|
149 else return e;
|
Chris@0
|
150 }
|
Chris@0
|
151
|
Chris@23
|
152 string checkLADSPAStyleDescriptorFn(void *f)
|
Chris@23
|
153 {
|
Chris@23
|
154 typedef const void *(*DFn)(unsigned long);
|
Chris@23
|
155 DFn fn = DFn(f);
|
Chris@23
|
156 unsigned long index = 0;
|
Chris@23
|
157 while (fn(index)) ++index;
|
Chris@23
|
158 if (index == 0) return "Library contains no plugins";
|
Chris@23
|
159 // else cerr << "Library contains " << index << " plugin(s)" << endl;
|
Chris@23
|
160 return "";
|
Chris@23
|
161 }
|
Chris@23
|
162
|
Chris@23
|
163 string checkVampDescriptorFn(void *f)
|
Chris@23
|
164 {
|
Chris@23
|
165 typedef const void *(*DFn)(unsigned int, unsigned int);
|
Chris@23
|
166 DFn fn = DFn(f);
|
Chris@23
|
167 unsigned int index = 0;
|
Chris@25
|
168 while (fn(2, index)) ++index;
|
Chris@23
|
169 if (index == 0) return "Library contains no plugins";
|
Chris@23
|
170 // else cerr << "Library contains " << index << " plugin(s)" << endl;
|
Chris@23
|
171 return "";
|
Chris@23
|
172 }
|
Chris@23
|
173
|
Chris@1
|
174 string check(string soname, string descriptor)
|
Chris@0
|
175 {
|
Chris@0
|
176 void *handle = DLOPEN(soname, RTLD_NOW | RTLD_LOCAL);
|
Chris@0
|
177 if (!handle) {
|
Chris@10
|
178 return "Unable to open plugin library: " + error();
|
Chris@0
|
179 }
|
Chris@0
|
180
|
Chris@0
|
181 void *fn = DLSYM(handle, descriptor);
|
Chris@0
|
182 if (!fn) {
|
Chris@10
|
183 return "Failed to find plugin descriptor " + descriptor +
|
Chris@10
|
184 " in library: " + error();
|
Chris@0
|
185 }
|
Chris@0
|
186
|
Chris@23
|
187 if (descriptor == "ladspa_descriptor") {
|
Chris@23
|
188 return checkLADSPAStyleDescriptorFn(fn);
|
Chris@23
|
189 } else if (descriptor == "dssi_descriptor") {
|
Chris@23
|
190 return checkLADSPAStyleDescriptorFn(fn);
|
Chris@23
|
191 } else if (descriptor == "vampGetPluginDescriptor") {
|
Chris@23
|
192 return checkVampDescriptorFn(fn);
|
Chris@23
|
193 } else {
|
Chris@23
|
194 cerr << "Note: no descriptor logic known for descriptor function \""
|
Chris@23
|
195 << descriptor << "\"; not actually calling it" << endl;
|
Chris@23
|
196 }
|
Chris@23
|
197
|
Chris@0
|
198 return "";
|
Chris@0
|
199 }
|
Chris@0
|
200
|
Chris@0
|
201 int main(int argc, char **argv)
|
Chris@0
|
202 {
|
Chris@0
|
203 bool allGood = true;
|
Chris@0
|
204 string soname;
|
Chris@0
|
205
|
Chris@27
|
206 bool showUsage = false;
|
Chris@27
|
207
|
Chris@27
|
208 if (argc > 1) {
|
Chris@27
|
209 string opt = argv[1];
|
Chris@27
|
210 if (opt == "-?" || opt == "-h" || opt == "--help") {
|
Chris@27
|
211 showUsage = true;
|
Chris@27
|
212 } else if (opt == "-v" || opt == "--version") {
|
Chris@27
|
213 cout << CHECKER_VERSION << endl;
|
Chris@27
|
214 return 0;
|
Chris@27
|
215 }
|
Chris@27
|
216 }
|
Chris@27
|
217
|
Chris@27
|
218 if (argc != 2 || showUsage) {
|
Chris@27
|
219 cerr << endl;
|
Chris@28
|
220 cerr << programName << ": Test shared library objects for plugins to be" << endl;
|
Chris@27
|
221 cerr << "loaded via descriptor functions." << endl;
|
Chris@28
|
222 cerr << "\n Usage: " << programName << " <descriptorname>\n"
|
Chris@11
|
223 "\nwhere descriptorname is the name of a plugin descriptor symbol to be sought\n"
|
Chris@11
|
224 "in each library (e.g. vampGetPluginDescriptor for Vamp plugins). The list of\n"
|
Chris@11
|
225 "candidate plugin library filenames is read from stdin.\n" << endl;
|
Chris@11
|
226 return 2;
|
Chris@1
|
227 }
|
Chris@1
|
228
|
Chris@1
|
229 string descriptor = argv[1];
|
Chris@1
|
230
|
Chris@0
|
231 while (getline(cin, soname)) {
|
Chris@11
|
232 string report = check(soname, descriptor);
|
Chris@11
|
233 if (report != "") {
|
Chris@11
|
234 cout << "FAILURE|" << soname << "|" << report << endl;
|
Chris@11
|
235 allGood = false;
|
Chris@11
|
236 } else {
|
Chris@11
|
237 cout << "SUCCESS|" << soname << "|" << endl;
|
Chris@11
|
238 }
|
Chris@0
|
239 }
|
Chris@11
|
240
|
Chris@0
|
241 return allGood ? 0 : 1;
|
Chris@0
|
242 }
|