annotate src/helper.cpp @ 36:ae74d39e9e4e plugin-path-config

Wide-char environment variable lookup
author Chris Cannam
date Fri, 08 Jun 2018 11:28:51 +0100
parents 146d42909e71
children a43d7a2867d2
rev   line source
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@32 231 #ifdef _WIN32
Chris@32 232 // Avoid showing the error-handler dialog for missing DLLs,
Chris@32 233 // failing quietly instead. It's permissible for this program
Chris@32 234 // to simply fail when a DLL can't be loaded -- showing the
Chris@32 235 // error dialog wouldn't change this anyway, it would just
Chris@32 236 // block the program until the user clicked it away and then
Chris@32 237 // fail anyway.
Chris@32 238 SetErrorMode(SEM_FAILCRITICALERRORS);
Chris@32 239 #endif
Chris@32 240
Chris@0 241 while (getline(cin, soname)) {
Chris@11 242 string report = check(soname, descriptor);
Chris@11 243 if (report != "") {
Chris@11 244 cout << "FAILURE|" << soname << "|" << report << endl;
Chris@11 245 allGood = false;
Chris@11 246 } else {
Chris@11 247 cout << "SUCCESS|" << soname << "|" << endl;
Chris@11 248 }
Chris@0 249 }
Chris@11 250
Chris@0 251 return allGood ? 0 : 1;
Chris@0 252 }