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 }