File: Modules.in.cpp

package info (click to toggle)
soapysdr 0.8.1-7
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 948 kB
  • sloc: cpp: 5,378; ansic: 471; python: 311; sh: 21; makefile: 18
file content (378 lines) | stat: -rw-r--r-- 11,671 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
// Copyright (c) 2014-2021 Josh Blum
// SPDX-License-Identifier: BSL-1.0

#include <SoapySDR/Modules.hpp>
#include <SoapySDR/Logger.hpp>
#include <SoapySDR/Version.hpp>
#include <vector>
#include <string>
#include <cstdlib> //getenv
#include <sstream>
#include <mutex>
#include <map>

#ifdef _WIN32
#include <windows.h>
#else
#include <dlfcn.h>
#include <glob.h>
#endif

static std::recursive_mutex &getModuleMutex(void)
{
    static std::recursive_mutex mutex;
    return mutex;
}

/***********************************************************************
 * root installation path
 **********************************************************************/
std::string getEnvImpl(const char *name)
{
    #ifdef _WIN32
    const DWORD len = GetEnvironmentVariableA(name, 0, 0);
    if (len == 0) return "";
    char* buffer = new char[len];
    GetEnvironmentVariableA(name, buffer, len);
    std::string result(buffer);
    delete [] buffer;
    return result;
    #else
    const char *result = getenv(name);
    if (result != NULL) return result;
    #endif
    return "";
}

std::string SoapySDR::getRootPath(void)
{
    const std::string rootPathEnv = getEnvImpl("@SOAPY_SDR_ROOT_ENV@");
    if (not rootPathEnv.empty()) return rootPathEnv;

    // Get the path to the current dynamic linked library.
    // The path to this library can be used to determine
    // the installation root without prior knowledge.
    #ifdef _WIN32
    char path[MAX_PATH];
    HMODULE hm = NULL;
    if (GetModuleHandleExA(
        GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS |
        GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
        (LPCSTR) &SoapySDR::getRootPath, &hm))
    {
        const DWORD size = GetModuleFileNameA(hm, path, sizeof(path));
        if (size != 0)
        {
            const std::string libPath(path, size);
            const size_t slash0Pos = libPath.find_last_of("/\\");
            const size_t slash1Pos = libPath.substr(0, slash0Pos).find_last_of("/\\");
            if (slash0Pos != std::string::npos and slash1Pos != std::string::npos)
                return libPath.substr(0, slash1Pos);
        }
    }
    #endif

    return "@SOAPY_SDR_ROOT@";
}

/***********************************************************************
 * list modules API call
 **********************************************************************/
static std::vector<std::string> searchModulePath(const std::string &path)
{
    std::vector<std::string> modulePaths;
    const std::string pattern = path + "*@MODULE_EXT@";

#ifdef _WIN32
    //http://stackoverflow.com/questions/612097/how-can-i-get-a-list-of-files-in-a-directory-using-c-or-c
    WIN32_FIND_DATA fd; 
    HANDLE hFind = ::FindFirstFile(pattern.c_str(), &fd); 
    if(hFind != INVALID_HANDLE_VALUE) 
    { 
        do 
        { 
            // read all (real) files in current folder
            // , delete '!' read other 2 default folder . and ..
            if(! (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ) 
            {
                modulePaths.push_back(path + fd.cFileName);
            }
        }while(::FindNextFile(hFind, &fd)); 
        ::FindClose(hFind); 
    }

#else
    glob_t globResults;

    const int ret = glob(pattern.c_str(), 0/*no flags*/, NULL, &globResults);
    if (ret == 0) for (size_t i = 0; i < globResults.gl_pathc; i++)
    {
        modulePaths.push_back(globResults.gl_pathv[i]);
    }
    else if (ret == GLOB_NOMATCH) {/* acceptable error condition, do not print error */}
    else SoapySDR::logf(SOAPY_SDR_ERROR, "SoapySDR::listModules(%s) glob(%s) error %d", path.c_str(), pattern.c_str(), ret);

    globfree(&globResults);

#endif

    return modulePaths;
}

std::vector<std::string> SoapySDR::listSearchPaths(void)
{
    //the default search path
    std::vector<std::string> searchPaths;
    searchPaths.push_back(SoapySDR::getRootPath() + "/@CMAKE_INSTALL_LIBDIR@/SoapySDR/modules" + SoapySDR::getABIVersion());

    //support /usr/local module installs when the install prefix is /usr
    if (SoapySDR::getRootPath() == "/usr")
    {
        searchPaths.push_back("/usr/local/@CMAKE_INSTALL_LIBDIR@/SoapySDR/modules" + SoapySDR::getABIVersion());
        //when using a multi-arch directory, support single-arch path as well
        static const std::string libdir("@CMAKE_INSTALL_LIBDIR@");
        if (libdir.find("lib/") == 0) //startswith lib/ indicating multi-arch
            searchPaths.push_back("/usr/local/lib/SoapySDR/modules" + SoapySDR::getABIVersion());
    }

    //separator for search paths
    #ifdef _WIN32
    static const char sep = ';';
    #else
    static const char sep = ':';
    #endif

    //check the environment's search path
    std::stringstream pluginPaths(getEnvImpl("SOAPY_SDR_PLUGIN_PATH"));
    std::string pluginPath;
    while (std::getline(pluginPaths, pluginPath, sep))
    {
        if (pluginPath.empty()) continue;
        searchPaths.push_back(pluginPath);
    }

    return searchPaths;
}

std::vector<std::string> SoapySDR::listModules(void)
{
    //traverse the search paths
    std::vector<std::string> modules;
    for (const auto &searchPath : SoapySDR::listSearchPaths())
    {
        const std::vector<std::string> subModules = SoapySDR::listModules(searchPath);
        modules.insert(modules.end(), subModules.begin(), subModules.end());
    }
    return modules;
}

std::vector<std::string> SoapySDR::listModules(const std::string &path)
{
    static const std::string suffix("@MODULE_EXT@");
    if (path.rfind(suffix) == (path.size()-suffix.size())) return {path};
    return searchModulePath(path + "/"); //requires trailing slash
}

/***********************************************************************
 * load module API call
 **********************************************************************/
std::map<std::string, void *> &getModuleHandles(void)
{
    static std::map<std::string, void *> handles;
    return handles;
}

//! share the module path during loadModule
std::string &getModuleLoading(void)
{
    static std::string moduleLoading;
    return moduleLoading;
}

//! share registration errors during loadModule
std::map<std::string, SoapySDR::Kwargs> &getLoaderResults(void)
{
    static std::map<std::string, SoapySDR::Kwargs> results;
    return results;
}

std::map<std::string, std::string> &getModuleVersions(void)
{
    static std::map<std::string, std::string> versions;
    return versions;
}

SoapySDR::ModuleVersion::ModuleVersion(const std::string &version)
{
    getModuleVersions()[getModuleLoading()] = version;
}

#ifdef _WIN32
static std::string GetLastErrorMessage(void)
{
    LPVOID lpMsgBuf;
    DWORD dw = GetLastError();

    FormatMessage(
        FORMAT_MESSAGE_ALLOCATE_BUFFER |
        FORMAT_MESSAGE_FROM_SYSTEM |
        FORMAT_MESSAGE_IGNORE_INSERTS,
        NULL,
        dw,
        MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
        (LPTSTR) &lpMsgBuf,
        0, NULL );

    std::string msg((char *)lpMsgBuf);
    LocalFree(lpMsgBuf);
    return msg;
}
#endif

static bool enableAutomaticLoadModules(true);

std::string SoapySDR::loadModule(const std::string &path)
{
    std::lock_guard<std::recursive_mutex> lock(getModuleMutex());

    //disable automatic load modules when individual modules are manually loaded
    enableAutomaticLoadModules = false;

    //check if already loaded
    if (getModuleHandles().count(path) != 0) return path + " already loaded";

    //stash the path for registry access
    getModuleLoading().assign(path);

    //load the module
#ifdef _WIN32

    //SetThreadErrorMode() - disable error pop-ups when DLLs are not found
    DWORD oldMode;
    SetThreadErrorMode(SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX | SEM_NOOPENFILEERRORBOX, &oldMode);
    HMODULE handle = LoadLibrary(path.c_str());
    SetThreadErrorMode(oldMode, nullptr);

    getModuleLoading().clear();
    if (handle == NULL) return "LoadLibrary() failed: " + GetLastErrorMessage();
#else
    void *handle = dlopen(path.c_str(), RTLD_LAZY);
    getModuleLoading().clear();
    if (handle == NULL) return "dlopen() failed: " + std::string(dlerror());
#endif

    //stash the handle
    getModuleHandles()[path] = handle;
    return "";
}

SoapySDR::Kwargs SoapySDR::getLoaderResult(const std::string &path)
{
    std::lock_guard<std::recursive_mutex> lock(getModuleMutex());
    if (getLoaderResults().count(path) == 0) return SoapySDR::Kwargs();
    return getLoaderResults()[path];
}

std::string SoapySDR::getModuleVersion(const std::string &path)
{
    std::lock_guard<std::recursive_mutex> lock(getModuleMutex());
    if (getModuleVersions().count(path) == 0) return "";
    return getModuleVersions()[path];
}

std::string SoapySDR::unloadModule(const std::string &path)
{
    std::lock_guard<std::recursive_mutex> lock(getModuleMutex());

    //check if already loaded
    if (getModuleHandles().count(path) == 0) return path + " never loaded";

    //stash the path for registry access
    getModuleLoading().assign(path);

    //unload the module
    void *handle = getModuleHandles()[path];
#ifdef _WIN32
    BOOL success = FreeLibrary((HMODULE)handle);
    getModuleLoading().clear();
    if (not success) return "FreeLibrary() failed: " + GetLastErrorMessage();
#else
    int status = dlclose(handle);
    getModuleLoading().clear();
    if (status != 0) return "dlclose() failed: " + std::string(dlerror());
#endif

    //clear the handle
    getLoaderResults().erase(path);
    getModuleVersions().erase(path);
    getModuleHandles().erase(path);
    return "";
}

/***********************************************************************
 * load modules API call
 **********************************************************************/

void lateLoadNullDevice(void);

void automaticLoadModules(void)
{
    std::lock_guard<std::recursive_mutex> lock(getModuleMutex());

    //loaded variable makes automatic load a one-shot
    static bool loaded = false;
    if (loaded) return;
    loaded = true;

    //initialize any static units in the library
    //rather than rely on static initialization
    lateLoadNullDevice();

    //load the modules when not otherwise disabled
    if (enableAutomaticLoadModules) SoapySDR::loadModules();
}

void SoapySDR::loadModules(void)
{
    std::lock_guard<std::recursive_mutex> lock(getModuleMutex());

    //initialize any static units in the library
    //rather than rely on static initialization
    lateLoadNullDevice();

    const auto paths = listModules();
    for (size_t i = 0; i < paths.size(); i++)
    {
        if (getModuleHandles().count(paths[i]) != 0) continue; //was manually loaded
        const std::string errorMsg = loadModule(paths[i]);
        if (not errorMsg.empty()) SoapySDR::logf(SOAPY_SDR_ERROR, "SoapySDR::loadModule(%s)\n  %s", paths[i].c_str(), errorMsg.c_str());
        for (const auto &it : SoapySDR::getLoaderResult(paths[i]))
        {
            if (it.second.empty()) continue;
            SoapySDR::logf(SOAPY_SDR_ERROR, "SoapySDR::loadModule(%s)\n  %s", paths[i].c_str(), it.second.c_str());
        }
    }
}

void SoapySDR::unloadModules(void)
{
    std::lock_guard<std::recursive_mutex> lock(getModuleMutex());
    for (auto it = getModuleHandles().begin(); it != getModuleHandles().end();)
    {
        auto path = it->first; //save path
        it++; //advance iterator
        auto msg = unloadModule(path);
        if (msg.empty()) continue;
        SoapySDR::logf(SOAPY_SDR_ERROR, "SoapySDR::unloadModule(%s)\n  %s", path.c_str(), msg.c_str());
    }
}

SoapySDR::ModuleManager::ModuleManager(const bool load)
{
    if (load) SoapySDR::loadModules();
}

SoapySDR::ModuleManager::~ModuleManager(void)
{
    SoapySDR::unloadModules();
}