UltraGrid
UltraGrid Low-Latency High-Resolution Transmission System
Loading...
Searching...
No Matches
lib_common.cpp
Go to the documentation of this file.
1
5/*
6 * Copyright (c) 2012-2023 CESNET, z. s. p. o.
7 * All rights reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, is permitted provided that the following conditions
11 * are met:
12 *
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 *
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 *
20 * 3. Neither the name of CESNET nor the names of its contributors may be
21 * used to endorse or promote products derived from this software without
22 * specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS
25 * "AS IS" AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING,
26 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
27 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
28 * EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
29 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
30 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
31 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
33 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
34 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
35 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36 */
37
38#ifdef HAVE_CONFIG_H
39#include "config.h"
40#include "config_unix.h"
41#include "config_win32.h"
42#endif // HAVE_CONFIG_H
43
44#ifdef BUILD_LIBRARIES
45#include <dlfcn.h>
46#include <glob.h>
47#include <libgen.h>
48#endif
49
50#include <iostream>
51#include <map>
52
53#include "debug.h"
54#include "host.h"
55#include "lib_common.h"
56#include "utils/color_out.h"
57
58#define MOD_NAME "[lib] "
59
60using namespace std;
61
63 const char *class_name;
64 const char *file_prefix;
65};
66
67const map<enum library_class, library_class_info_t> library_class_info = {
68 { LIBRARY_CLASS_UNDEFINED, { "General module", "" }},
69 { LIBRARY_CLASS_CAPTURE_FILTER, { "Video capture filter" , "vcapfilter" }},
70 { LIBRARY_CLASS_AUDIO_CAPTURE, { "Audio capture device", "acap" }},
71 { LIBRARY_CLASS_AUDIO_PLAYBACK, { "Audio playback device", "aplay" }},
72 { LIBRARY_CLASS_VIDEO_CAPTURE, { "Video capture device", "vidcap" }},
73 { LIBRARY_CLASS_VIDEO_DISPLAY, { "Video display device", "display" }},
74 { LIBRARY_CLASS_AUDIO_COMPRESS, { "Audio compression", "acompress" }},
75 { LIBRARY_CLASS_VIDEO_DECOMPRESS, { "Video decompression", "vdecompress" }},
76 { LIBRARY_CLASS_VIDEO_COMPRESS, { "Video compression", "vcompress" }},
77 { LIBRARY_CLASS_VIDEO_POSTPROCESS, { "Video postprocess", "vo_pp" }},
78 { LIBRARY_CLASS_VIDEO_RXTX, { "Video RXTX", "video_rxtx" }},
79};
80
81static map<string, string> lib_errors;
82
83#ifdef BUILD_LIBRARIES
84static void push_basename_entry(char ***binarynames, const char *bnc, size_t * templates) {
85 char * alt_v0 = strdup(bnc);
86 assert(alt_v0 != NULL);
87 char * bn_v0 = basename(alt_v0);
88 for (size_t i = 0; i < *templates && bn_v0 != NULL; ++i) {
89 if (strcmp((*binarynames)[i], bn_v0) == 0)
90 bn_v0 = NULL;
91 }
92 if (bn_v0 != NULL) {
93 *binarynames = (char **)realloc(*binarynames, (*templates + 2)*sizeof(**binarynames));
94 assert(*binarynames != NULL);
95 (*binarynames)[(*templates)++] = strdup(bn_v0);
96 (*binarynames)[*templates] = NULL;
97 }
98 free(alt_v0);
99}
100
101static int running_from_path(char * uv_argv[]) {
102 const char *env_path = getenv("PATH");
103 if (env_path == NULL) {
104 // LOG(LOG_LEVEL_WARNING) << "Failed to determine environment PATH variable, plugins shall be searched relative to binary" << "\n";
105 return 0;
106 }
107
108 char *self_path = realpath("/proc/self/exe", NULL);
109 if (self_path == NULL) {
110 // LOG(LOG_LEVEL_WARNING) << "Failed to determine path to running process" << "\n";
111 return 0;
112 }
113
114 // considered binary names, currently only uv, but reserve other as well
115 const char * binarynames_tpl[] = {
116 "uv",
117 "ultragrid",
118 "uv-nightly",
119 "ultragrid-nightly"
120 };
121
122 size_t templates = sizeof(binarynames_tpl)/sizeof(binarynames_tpl[0]);
123 char **binarynames = (char **)calloc(templates + 1 + 3, sizeof(*binarynames)); // pre-allocate 3 dirnames
124 assert(binarynames != NULL);
125 for (size_t i = 0; i < templates; ++i)
126 binarynames[i] = strdup(binarynames_tpl[i]);
127
128 push_basename_entry(&binarynames, uv_argv[0], &templates);
129 if (strchr(uv_argv[0], '/') != NULL) {
130 char * alt_v0 = realpath(uv_argv[0], NULL);
131 if (alt_v0 != NULL) {
132 push_basename_entry(&binarynames, alt_v0, &templates);
133 free(alt_v0);
134 }
135 }
136 push_basename_entry(&binarynames, self_path, &templates);
137
138 char *pathdup = strdup(env_path);
139 char *tokencont = pathdup;
140 int rval = 0;
141
142 // if path contains /, make it the first and only candidate
143
144 char *next_token = NULL;
145 while (rval == 0) {
146 // loop through paths
147 const char *pathelem = strtok_r(tokencont, ":", &next_token);
148 tokencont = NULL;
149 if (pathelem == NULL)
150 break;
151
152 for (size_t i = 0; binarynames[i] != NULL && rval == 0; ++i) {
153 const size_t len = strlen(pathelem) + 1 + strlen(binarynames[i]) + 1;
154 char * candidate = (char *)calloc(1, len);
155 snprintf(candidate, len, "%s/%s", pathelem, binarynames[i]);
156
157 char * real_candidate = realpath(candidate, NULL);
158 if (real_candidate != NULL) {
159 rval = strcmp(real_candidate, self_path) == 0;
160 free(real_candidate);
161 }
162 free(candidate);
163 }
164 };
165
166 for (size_t i = 0; binarynames[i] != NULL; ++i)
167 free(binarynames[i]);
168 free(binarynames);
169
170 free(pathdup);
171 free(self_path);
172 return rval;
173}
174#endif
175
176void open_all(const char *pattern, list<void *> &libs) {
177#ifdef BUILD_LIBRARIES
178 char path[512];
179 glob_t glob_buf;
180
181 /* binary not from $PATH */
182 if (!running_from_path(uv_argv)) {
183 char *tmp = strdup(uv_argv[0]);
184 char *dir = dirname(tmp);
185 snprintf(path, sizeof(path), "%s/../lib/ultragrid/%s", dir, pattern);
186 free(tmp);
187 } else {
188 snprintf(path, sizeof(path), LIB_DIR "/ultragrid/%s", pattern);
189 }
190
191 glob(path, 0, NULL, &glob_buf);
192
193 for(unsigned int i = 0; i < glob_buf.gl_pathc; ++i) {
194 void *handle = dlopen(glob_buf.gl_pathv[i], RTLD_NOW|RTLD_GLOBAL);
195 if (!handle) {
196 char *error = dlerror();
197 MSG(WARNING, "Library %s opening warning: %s \n",
198 glob_buf.gl_pathv[i], error);
199 char *filename = basename(glob_buf.gl_pathv[i]);
200 if (filename && error) {
201 lib_errors.emplace(filename, error);
202 }
203 continue;
204 }
205 libs.push_back(handle);
206 }
207
208 globfree(&glob_buf);
209#else
210 UNUSED(libs);
212#endif
213}
214
215struct lib_info {
216 const void *data;
218 bool hidden;
219};
220
221// http://stackoverflow.com/questions/1801892/making-mapfind-operation-case-insensitive
222/************************************************************************/
223/* Comparator for case-insensitive comparison in STL assos. containers */
224/************************************************************************/
226{
227 // case-independent (ci) compare_less binary function
229 {
230 bool operator() (const unsigned char& c1, const unsigned char& c2) const {
231 return tolower (c1) < tolower (c2);
232 }
233 };
234 bool operator() (const std::string & s1, const std::string & s2) const {
235 return std::lexicographical_compare
236 (s1.begin (), s1.end (), // source range
237 s2.begin (), s2.end (), // dest range
238 nocase_compare ()); // comparison
239 }
240};
241
242static auto& get_libmap(){
243 /* This is needed because register_library() may be called before global
244 * static members are initialized (it is __attribute__((constructor)))
245 */
246 static map<enum library_class, map<string, lib_info, ci_less>> libraries;
247 return libraries;
248}
249
250void register_library(const char *name, const void *data, enum library_class cls, int abi_version, int hidden)
251{
252 auto& map = get_libmap()[cls];
253 if (map.find(name) != map.end()) {
254 LOG(LOG_LEVEL_ERROR) << "Module \"" << name << "\" (class " << cls << ") multiple initialization!\n";
255 }
256 map[name] = {data, abi_version, static_cast<bool>(hidden)};
257}
258
259const void *load_library(const char *name, enum library_class cls, int abi_version)
260{
261 auto it_cls = get_libmap().find(cls);
262 if (it_cls != get_libmap().end()) {
263 auto it_module = it_cls->second.find(name);
264 if (it_module != it_cls->second.end()) {
265 const auto& mod_pair = it_module->second;
266 if (mod_pair.abi_version == abi_version) {
267 return mod_pair.data;
268 } else {
269 LOG(LOG_LEVEL_WARNING) << "Module " << name << " ABI version mismatch (required " <<
270 abi_version << ", have " << mod_pair.abi_version << ")\n";
271 }
272 }
273 }
274
275 // Library was not found or was not loaded due to unsatisfied
276 // dependencies. If the latter one, display reason why dlopen() failed.
277 if (library_class_info.find(cls) != library_class_info.end()) {
278 string filename = "ultragrid_";
279 if (strlen(library_class_info.at(cls).file_prefix) > 0) {
280 filename += library_class_info.at(cls).file_prefix;
281 filename += "_";
282 }
283
284 filename += name + string(".so");
285
286 if (lib_errors.find(filename) != lib_errors.end()) {
287 LOG(LOG_LEVEL_WARNING) << filename << ": " << lib_errors.find(filename)->second << "\n";
288 }
289 }
290
291 return NULL;
292}
293
298void list_modules(enum library_class cls, int abi_version, bool full) {
299 const auto & class_set = get_libraries_for_class(cls, abi_version, full);
300 for (auto && item : class_set) {
301 col() << "\t" << SBOLD(item.first.c_str()) << "\n";
302 }
303}
304
309 bool ret = true;
310
311 auto& libraries = get_libmap();
312 for (auto cls_it = library_class_info.begin(); cls_it != library_class_info.end();
313 ++cls_it) {
314 cout << cls_it->second.class_name << "\n";
315 auto it = libraries.find(cls_it->first);
316 if (it != libraries.end()) {
317 for (auto && item : it->second) {
318 col() << "\t" << SBOLD(item.first) << "\n";
319 }
320 }
321 cout << "\n";
322 }
323
324 if (!lib_errors.empty()) {
325 ret = false;
326 col() << SBOLD(SRED("Errors:")) << "\n";
327 for (auto && item : lib_errors) {
328 cout << "\t" << SRED(item.first) << "\n\t\t" << item.second << "\n";
329 }
330 cout << "\n";
331 }
332
333 return ret;
334}
335
336map<string, const void *> get_libraries_for_class(enum library_class cls, int abi_version, bool include_hidden)
337{
338 map<string, const void *> ret;
339 auto& libraries = get_libmap();
340 auto it = libraries.find(cls);
341 if (it != libraries.end()) {
342 for (auto && item : it->second) {
343 if (abi_version == item.second.abi_version) {
344 if (include_hidden || !item.second.hidden) {
345 ret[item.first] = item.second.data;
346 }
347 } else {
348 LOG(LOG_LEVEL_WARNING) << "Module " << item.first << " ABI version mismatch (required " <<
349 abi_version << ", have " << item.second.abi_version << ")\n";
350
351 }
352 }
353 }
354
355 return ret;
356}
357
#define UNUSED(x)
int void size_t * len
pattern
Definition testcard.c:94
#define SRED(x)
Definition color_out.h:85
#define SBOLD(x)
Definition color_out.h:80
#define LOG_LEVEL_ERROR
general errors
Definition debug.h:59
#define MSG(l, fmt,...)
Definition debug.h:96
#define LOG(level)
Definition debug.h:313
#define LOG_LEVEL_WARNING
less severe errors
Definition debug.h:60
enum library_class cls
Definition host.cpp:749
char ** uv_argv
Definition host.cpp:174
map< string, const void * > get_libraries_for_class(enum library_class cls, int abi_version, bool include_hidden)
void list_modules(enum library_class cls, int abi_version, bool full)
const map< enum library_class, library_class_info_t > library_class_info
static map< string, string > lib_errors
static auto & get_libmap()
bool list_all_modules()
void register_library(const char *name, const void *data, enum library_class cls, int abi_version, int hidden)
void open_all(const char *pattern, list< void * > &libs)
const void * load_library(const char *name, enum library_class cls, int abi_version)
library_class
Definition lib_common.h:73
@ LIBRARY_CLASS_VIDEO_POSTPROCESS
Definition lib_common.h:83
@ LIBRARY_CLASS_VIDEO_DECOMPRESS
Definition lib_common.h:81
@ LIBRARY_CLASS_VIDEO_CAPTURE
Definition lib_common.h:78
@ LIBRARY_CLASS_AUDIO_PLAYBACK
Definition lib_common.h:77
@ LIBRARY_CLASS_VIDEO_RXTX
Definition lib_common.h:84
@ LIBRARY_CLASS_AUDIO_CAPTURE
Definition lib_common.h:76
@ LIBRARY_CLASS_VIDEO_DISPLAY
Definition lib_common.h:79
@ LIBRARY_CLASS_UNDEFINED
Definition lib_common.h:74
@ LIBRARY_CLASS_CAPTURE_FILTER
Definition lib_common.h:75
@ LIBRARY_CLASS_AUDIO_COMPRESS
Definition lib_common.h:80
@ LIBRARY_CLASS_VIDEO_COMPRESS
Definition lib_common.h:82
const char * name
Definition settings.cpp:193
bool operator()(const unsigned char &c1, const unsigned char &c2) const
bool operator()(const std::string &s1, const std::string &s2) const
Definition hd-rum.c:27
int abi_version
const void * data
const char * file_prefix
const char * class_name