Mercurial > hg > vamp-plugin-load-checker
comparison src/helper.cpp @ 45:ad02dff8ebfb
Merge from branch errorcode
author | Chris Cannam |
---|---|
date | Fri, 31 Aug 2018 15:14:57 +0100 |
parents | 49946b02414e |
children | d7ec0b2a8802 |
comparison
equal
deleted
inserted
replaced
39:a28b19b136e8 | 45:ad02dff8ebfb |
---|---|
17 * | 17 * |
18 * Output line for successful load of library libname.so: | 18 * Output line for successful load of library libname.so: |
19 * SUCCESS|/path/to/libname.so| | 19 * SUCCESS|/path/to/libname.so| |
20 * | 20 * |
21 * Output line for failed load of library libname.so: | 21 * Output line for failed load of library libname.so: |
22 * FAILURE|/path/to/libname.so|Reason for failure if available | 22 * FAILURE|/path/to/libname.so|Error message [failureCode] |
23 * | |
24 * or: | |
25 * FAILURE|/path/to/libname.so|[failureCode] | |
26 * | |
27 * where the error message is an optional system-level message, such | |
28 * as may be returned from strerror or similar (which should be in the | |
29 * native language for the system ready to show the user), and the | |
30 * failureCode in square brackets is a mandatory number corresponding | |
31 * to one of the PluginCandidates::FailureCode values (requiring | |
32 * conversion to a translated string by the client). | |
23 * | 33 * |
24 * Although this program was written for use with Vamp audio analysis | 34 * Although this program was written for use with Vamp audio analysis |
25 * plugins, it also works with other plugin formats. The program has | 35 * plugins, it also works with other plugin formats. The program has |
26 * some hardcoded knowledge of Vamp, LADSPA, and DSSI plugins, but it | 36 * some hardcoded knowledge of Vamp, LADSPA, and DSSI plugins, but it |
27 * can be used with any plugins that involve loading DLLs and looking | 37 * can be used with any plugins that involve loading DLLs and looking |
62 dealings in this Software without prior written authorization. | 72 dealings in this Software without prior written authorization. |
63 */ | 73 */ |
64 | 74 |
65 #include "../version.h" | 75 #include "../version.h" |
66 | 76 |
77 #include "../checker/checkcode.h" | |
78 | |
67 static const char programName[] = "vamp-plugin-load-checker"; | 79 static const char programName[] = "vamp-plugin-load-checker"; |
68 | 80 |
69 #ifdef _WIN32 | 81 #ifdef _WIN32 |
70 #include <windows.h> | 82 #include <windows.h> |
71 #include <process.h> | 83 #include <process.h> |
76 | 88 |
77 #ifdef _WIN32 | 89 #ifdef _WIN32 |
78 #ifndef UNICODE | 90 #ifndef UNICODE |
79 #error "This must be compiled with UNICODE defined" | 91 #error "This must be compiled with UNICODE defined" |
80 #endif | 92 #endif |
93 | |
81 static std::string lastLibraryName = ""; | 94 static std::string lastLibraryName = ""; |
82 static HMODULE LoadLibraryUTF8(std::string name) { | 95 |
96 static HMODULE loadLibraryUTF8(std::string name) { | |
83 lastLibraryName = name; | 97 lastLibraryName = name; |
84 int n = name.size(); | 98 int n = name.size(); |
85 int wn = MultiByteToWideChar(CP_UTF8, 0, name.c_str(), n, 0, 0); | 99 int wn = MultiByteToWideChar(CP_UTF8, 0, name.c_str(), n, 0, 0); |
86 wchar_t *wname = new wchar_t[wn+1]; | 100 wchar_t *wname = new wchar_t[wn+1]; |
87 wn = MultiByteToWideChar(CP_UTF8, 0, name.c_str(), n, wname, wn); | 101 wn = MultiByteToWideChar(CP_UTF8, 0, name.c_str(), n, wname, wn); |
88 wname[wn] = L'\0'; | 102 wname[wn] = L'\0'; |
89 HMODULE h = LoadLibraryW(wname); | 103 HMODULE h = LoadLibraryW(wname); |
90 delete[] wname; | 104 delete[] wname; |
91 return h; | 105 return h; |
92 } | 106 } |
93 static std::string GetErrorText() { | 107 |
108 static std::string getErrorText() { | |
94 DWORD err = GetLastError(); | 109 DWORD err = GetLastError(); |
95 wchar_t *buffer; | 110 wchar_t *buffer = 0; |
96 FormatMessageW( | 111 FormatMessageW( |
97 FORMAT_MESSAGE_ALLOCATE_BUFFER | | 112 FORMAT_MESSAGE_ALLOCATE_BUFFER | |
98 FORMAT_MESSAGE_FROM_SYSTEM | | 113 FORMAT_MESSAGE_FROM_SYSTEM | |
99 FORMAT_MESSAGE_IGNORE_INSERTS, | 114 FORMAT_MESSAGE_IGNORE_INSERTS, |
100 NULL, | 115 NULL, |
101 err, | 116 err, |
117 // the correct way to specify the user's default language, | |
118 // according to all resources I could find: | |
102 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), | 119 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), |
103 (LPWSTR) &buffer, | 120 (LPWSTR) &buffer, |
104 0, NULL ); | 121 0, NULL ); |
122 if (!buffer) { | |
123 return "Unable to format error string (internal error)"; | |
124 } | |
105 int wn = wcslen(buffer); | 125 int wn = wcslen(buffer); |
106 int n = WideCharToMultiByte(CP_UTF8, 0, buffer, wn, 0, 0, 0, 0); | 126 int n = WideCharToMultiByte(CP_UTF8, 0, buffer, wn, 0, 0, 0, 0); |
107 if (n < 0) { | 127 if (n < 0) { |
108 LocalFree(&buffer); | 128 LocalFree(&buffer); |
109 return "Unable to convert error string (internal error)"; | 129 return "Unable to convert error string (internal error)"; |
112 (void)WideCharToMultiByte(CP_UTF8, 0, buffer, wn, text, n, 0, 0); | 132 (void)WideCharToMultiByte(CP_UTF8, 0, buffer, wn, text, n, 0, 0); |
113 text[n] = '\0'; | 133 text[n] = '\0'; |
114 std::string s(text); | 134 std::string s(text); |
115 LocalFree(&buffer); | 135 LocalFree(&buffer); |
116 delete[] text; | 136 delete[] text; |
137 if (s == "") { | |
138 return s; | |
139 } | |
117 for (int i = s.size(); i > 0; ) { | 140 for (int i = s.size(); i > 0; ) { |
118 --i; | 141 --i; |
119 if (s[i] == '\n' || s[i] == '\r') { | 142 if (s[i] == '\n' || s[i] == '\r') { |
120 s.erase(i, 1); | 143 s.erase(i, 1); |
121 } | 144 } |
124 if (pos != std::string::npos && lastLibraryName != "") { | 147 if (pos != std::string::npos && lastLibraryName != "") { |
125 s.replace(pos, 2, lastLibraryName); | 148 s.replace(pos, 2, lastLibraryName); |
126 } | 149 } |
127 return s; | 150 return s; |
128 } | 151 } |
129 #define DLOPEN(a,b) LoadLibraryUTF8(a) | 152 |
153 #define DLOPEN(a,b) loadLibraryUTF8(a) | |
130 #define DLSYM(a,b) (void *)GetProcAddress((HINSTANCE)(a),(b).c_str()) | 154 #define DLSYM(a,b) (void *)GetProcAddress((HINSTANCE)(a),(b).c_str()) |
131 #define DLCLOSE(a) (!FreeLibrary((HINSTANCE)(a))) | 155 #define DLCLOSE(a) (!FreeLibrary((HINSTANCE)(a))) |
132 #define DLERROR() (GetErrorText()) | 156 #define DLERROR() (getErrorText()) |
157 | |
158 static bool libraryExists(std::string name) { | |
159 if (name == "") return false; | |
160 int n = name.size(); | |
161 int wn = MultiByteToWideChar(CP_UTF8, 0, name.c_str(), n, 0, 0); | |
162 wchar_t *wname = new wchar_t[wn+1]; | |
163 wn = MultiByteToWideChar(CP_UTF8, 0, name.c_str(), n, wname, wn); | |
164 wname[wn] = L'\0'; | |
165 FILE *f = _wfopen(wname, L"rb"); | |
166 delete[] wname; | |
167 if (f) { | |
168 fclose(f); | |
169 return true; | |
170 } else { | |
171 return false; | |
172 } | |
173 } | |
174 | |
133 #else | 175 #else |
176 | |
134 #include <dlfcn.h> | 177 #include <dlfcn.h> |
135 #define DLOPEN(a,b) dlopen((a).c_str(),(b)) | 178 #define DLOPEN(a,b) dlopen((a).c_str(),(b)) |
136 #define DLSYM(a,b) dlsym((a),(b).c_str()) | 179 #define DLSYM(a,b) dlsym((a),(b).c_str()) |
137 #define DLCLOSE(a) dlclose((a)) | 180 #define DLCLOSE(a) dlclose((a)) |
138 #define DLERROR() dlerror() | 181 #define DLERROR() dlerror() |
182 | |
183 static bool libraryExists(std::string name) { | |
184 if (name == "") return false; | |
185 FILE *f = fopen(name.c_str(), "r"); | |
186 if (f) { | |
187 fclose(f); | |
188 return true; | |
189 } else { | |
190 return false; | |
191 } | |
192 } | |
193 | |
139 #endif | 194 #endif |
140 | 195 |
141 //#include <unistd.h> | 196 //#include <unistd.h> |
142 | 197 |
143 using namespace std; | 198 using namespace std; |
144 | 199 |
145 string error() | 200 string error() |
146 { | 201 { |
147 string e = DLERROR(); | 202 return DLERROR(); |
148 if (e == "") return "(unknown error)"; | 203 } |
149 else return e; | 204 |
150 } | 205 struct Result { |
151 | 206 PluginCheckCode code; |
152 string checkLADSPAStyleDescriptorFn(void *f) | 207 string message; |
208 }; | |
209 | |
210 Result checkLADSPAStyleDescriptorFn(void *f) | |
153 { | 211 { |
154 typedef const void *(*DFn)(unsigned long); | 212 typedef const void *(*DFn)(unsigned long); |
155 DFn fn = DFn(f); | 213 DFn fn = DFn(f); |
156 unsigned long index = 0; | 214 unsigned long index = 0; |
157 while (fn(index)) ++index; | 215 while (fn(index)) ++index; |
158 if (index == 0) return "Library contains no plugins"; | 216 if (index == 0) return { PluginCheckCode::FAIL_NO_PLUGINS, "" }; |
159 return ""; | 217 return { PluginCheckCode::SUCCESS, "" }; |
160 } | 218 } |
161 | 219 |
162 string checkVampDescriptorFn(void *f) | 220 Result checkVampDescriptorFn(void *f) |
163 { | 221 { |
164 typedef const void *(*DFn)(unsigned int, unsigned int); | 222 typedef const void *(*DFn)(unsigned int, unsigned int); |
165 DFn fn = DFn(f); | 223 DFn fn = DFn(f); |
166 unsigned int index = 0; | 224 unsigned int index = 0; |
167 while (fn(2, index)) ++index; | 225 while (fn(2, index)) ++index; |
168 if (index == 0) return "Library contains no plugins"; | 226 if (index == 0) return { PluginCheckCode::FAIL_NO_PLUGINS, "" }; |
169 return ""; | 227 return { PluginCheckCode::SUCCESS, "" }; |
170 } | 228 } |
171 | 229 |
172 string check(string soname, string descriptor) | 230 Result check(string soname, string descriptor) |
173 { | 231 { |
174 void *handle = DLOPEN(soname, RTLD_NOW | RTLD_LOCAL); | 232 void *handle = DLOPEN(soname, RTLD_NOW | RTLD_LOCAL); |
175 if (!handle) { | 233 if (!handle) { |
176 return "Unable to open plugin library: " + error(); | 234 PluginCheckCode code = PluginCheckCode::FAIL_NOT_LOADABLE; |
177 } | 235 string message = error(); |
178 | 236 #ifdef _WIN32 |
179 string msg = ""; | 237 DWORD err = GetLastError(); |
238 if (err == ERROR_BAD_EXE_FORMAT) { | |
239 code = PluginCheckCode::FAIL_WRONG_ARCHITECTURE; | |
240 } else if (err == ERROR_MOD_NOT_FOUND) { | |
241 if (libraryExists(soname)) { | |
242 code = PluginCheckCode::FAIL_DEPENDENCY_MISSING; | |
243 } else { | |
244 code = PluginCheckCode::FAIL_LIBRARY_NOT_FOUND; | |
245 } | |
246 } | |
247 #else | |
248 if (!libraryExists(soname)) { | |
249 code = PluginCheckCode::FAIL_LIBRARY_NOT_FOUND; | |
250 } | |
251 #endif | |
252 return { code, message }; | |
253 } | |
254 | |
255 Result result { PluginCheckCode::SUCCESS, "" }; | |
180 | 256 |
181 void *fn = DLSYM(handle, descriptor); | 257 void *fn = DLSYM(handle, descriptor); |
182 if (!fn) { | 258 if (!fn) { |
183 msg = "Failed to find plugin descriptor " + descriptor + | 259 result = { PluginCheckCode::FAIL_DESCRIPTOR_MISSING, error() }; |
184 " in library: " + error(); | |
185 } else if (descriptor == "ladspa_descriptor") { | 260 } else if (descriptor == "ladspa_descriptor") { |
186 msg = checkLADSPAStyleDescriptorFn(fn); | 261 result = checkLADSPAStyleDescriptorFn(fn); |
187 } else if (descriptor == "dssi_descriptor") { | 262 } else if (descriptor == "dssi_descriptor") { |
188 msg = checkLADSPAStyleDescriptorFn(fn); | 263 result = checkLADSPAStyleDescriptorFn(fn); |
189 } else if (descriptor == "vampGetPluginDescriptor") { | 264 } else if (descriptor == "vampGetPluginDescriptor") { |
190 msg = checkVampDescriptorFn(fn); | 265 result = checkVampDescriptorFn(fn); |
191 } else { | 266 } else { |
192 cerr << "Note: no descriptor logic known for descriptor function \"" | 267 cerr << "Note: no descriptor logic known for descriptor function \"" |
193 << descriptor << "\"; not actually calling it" << endl; | 268 << descriptor << "\"; not actually calling it" << endl; |
194 } | 269 } |
195 | 270 |
196 DLCLOSE(handle); | 271 DLCLOSE(handle); |
197 | 272 |
198 return msg; | 273 return result; |
199 } | 274 } |
200 | 275 |
201 int main(int argc, char **argv) | 276 int main(int argc, char **argv) |
202 { | 277 { |
203 bool allGood = true; | 278 bool allGood = true; |
208 if (argc > 1) { | 283 if (argc > 1) { |
209 string opt = argv[1]; | 284 string opt = argv[1]; |
210 if (opt == "-?" || opt == "-h" || opt == "--help") { | 285 if (opt == "-?" || opt == "-h" || opt == "--help") { |
211 showUsage = true; | 286 showUsage = true; |
212 } else if (opt == "-v" || opt == "--version") { | 287 } else if (opt == "-v" || opt == "--version") { |
213 cout << CHECKER_VERSION << endl; | 288 cout << CHECKER_COMPATIBILITY_VERSION << endl; |
214 return 0; | 289 return 0; |
215 } | 290 } |
216 } | 291 } |
217 | 292 |
218 if (argc != 2 || showUsage) { | 293 if (argc != 2 || showUsage) { |
237 // fail anyway. | 312 // fail anyway. |
238 SetErrorMode(SEM_FAILCRITICALERRORS); | 313 SetErrorMode(SEM_FAILCRITICALERRORS); |
239 #endif | 314 #endif |
240 | 315 |
241 while (getline(cin, soname)) { | 316 while (getline(cin, soname)) { |
242 string report = check(soname, descriptor); | 317 Result result = check(soname, descriptor); |
243 if (report != "") { | 318 if (result.code == PluginCheckCode::SUCCESS) { |
244 cout << "FAILURE|" << soname << "|" << report << endl; | 319 cout << "SUCCESS|" << soname << "|" << endl; |
320 } else { | |
321 if (result.message == "") { | |
322 cout << "FAILURE|" << soname | |
323 << "|[" << int(result.code) << "]" << endl; | |
324 } else { | |
325 cout << "FAILURE|" << soname | |
326 << "|" << result.message << " [" | |
327 << int(result.code) << "]" << endl; | |
328 } | |
245 allGood = false; | 329 allGood = false; |
246 } else { | |
247 cout << "SUCCESS|" << soname << "|" << endl; | |
248 } | 330 } |
249 } | 331 } |
250 | 332 |
251 return allGood ? 0 : 1; | 333 return allGood ? 0 : 1; |
252 } | 334 } |