This source file includes following definitions.
- _sir_plugin_load
- _sir_plugin_probe
- _sir_plugin_getexport
- _sir_plugin_unload
- _sir_plugin_add
- _sir_plugin_rem
- _sir_plugin_destroy
- _sir_plugin_cache_pred_id
- _sir_plugin_cache_add
- _sir_plugin_cache_find_id
- _sir_plugin_cache_find
- _sir_plugin_cache_rem
- _sir_plugin_cache_destroy
- _sir_plugin_cache_dispatch
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 #include "sir/plugins.h"
33 #include "sir/internal.h"
34
35 sirpluginid _sir_plugin_load(const char* path) {
36 #if !defined(SIR_NO_PLUGINS)
37 (void)_sir_seterror(_SIR_E_NOERROR);
38
39 if (!_sir_sanity() || !_sir_validstr(path))
40 return 0U;
41
42 sir_plugin* plugin = (sir_plugin*)calloc(1, sizeof(sir_plugin));
43 if (!plugin)
44 return _sir_handleerr(errno);
45
46 # if !defined (__WIN__)
47 plugin->handle = dlopen(path, RTLD_NOW | RTLD_LOCAL);
48 if (!plugin->handle) {
49 const char* err = dlerror();
50 _sir_selflog("error: dlopen('%s') failed (%s)", path, _SIR_PRNSTR(err));
51 _sir_plugin_destroy(&plugin);
52 return _sir_handleerr(errno);
53 }
54 # else
55 UINT old_error_mode = SetErrorMode(SEM_FAILCRITICALERRORS);
56 plugin->handle = LoadLibraryA(path);
57 SetErrorMode(old_error_mode);
58 if (!plugin->handle) {
59 DWORD err = GetLastError();
60 _sir_selflog("error: LoadLibraryA(%s) failed (%lu)", path, err);
61 _sir_plugin_destroy(&plugin);
62 return _sir_handlewin32err(err);
63 }
64 # endif
65
66 plugin->loaded = true;
67 plugin->path = strndup(path, strnlen(path, SIR_MAXPATH));
68
69 if (!_sir_validstrnofail(plugin->path)) {
70 _sir_plugin_destroy(&plugin);
71 return _sir_handleerr(errno);
72 }
73
74 _sir_selflog("loaded plugin (path: '%s', addr: %p); probing...",
75 plugin->path, plugin->handle);
76
77 return _sir_plugin_probe(plugin);
78 #else
79 SIR_UNUSED(path);
80 return 0U;
81 #endif
82 }
83
84 sirpluginid _sir_plugin_probe(sir_plugin* plugin) {
85 #if !defined(SIR_NO_PLUGINS)
86 sirpluginid retval = 0U;
87 if (plugin) {
88 # if SIR_PLUGIN_VCURRENT == SIR_PLUGIN_V1
89
90
91
92
93
94
95
96
97
98 plugin->iface.query = (sir_plugin_queryfn)
99 _sir_plugin_getexport(plugin->handle, SIR_PLUGIN_EXPORT_QUERY);
100 plugin->iface.init = (sir_plugin_initfn)
101 _sir_plugin_getexport(plugin->handle, SIR_PLUGIN_EXPORT_INIT);
102 plugin->iface.write = (sir_plugin_writefn)
103 _sir_plugin_getexport(plugin->handle, SIR_PLUGIN_EXPORT_WRITE);
104 plugin->iface.cleanup = (sir_plugin_cleanupfn)
105 _sir_plugin_getexport(plugin->handle, SIR_PLUGIN_EXPORT_CLEANUP);
106
107 if (!plugin->iface.query || !plugin->iface.init ||
108 !plugin->iface.write || !plugin->iface.cleanup) {
109 _sir_selflog("error: export(s) not resolved for plugin (path:"
110 " '%s', addr: %p)!", plugin->path, plugin->handle);
111 _sir_selflog("exports (query: %"PRIxPTR", init: %"PRIxPTR", write:"
112 " %"PRIxPTR", cleanup; %"PRIxPTR")",
113 (uintptr_t)plugin->iface.query, (uintptr_t)plugin->iface.init,
114 (uintptr_t)plugin->iface.write, (uintptr_t)plugin->iface.cleanup);
115 _sir_plugin_destroy(&plugin);
116 return _sir_seterror(_SIR_E_PLUGINBAD);
117 }
118 # else
119 # error "plugin version not implemented"
120 # endif
121
122 if (!plugin->iface.query(&plugin->info)) {
123 _sir_selflog("error: plugin (path: '%s', addr: %p) returned false from"
124 " query fn!", plugin->path, plugin->handle);
125 _sir_plugin_destroy(&plugin);
126 (void)_sir_seterror(_SIR_E_PLUGINERR);
127 return 0U;
128 }
129
130
131 if (!plugin->info.iface_ver || plugin->info.iface_ver > SIR_PLUGIN_VCURRENT) {
132 _sir_selflog("error: plugin (path: '%s', addr: %p) has version"
133 " %"PRIu8"; libsir has %d", plugin->path, plugin->handle,
134 plugin->info.iface_ver, SIR_PLUGIN_VCURRENT);
135 _sir_plugin_destroy(&plugin);
136 (void)_sir_seterror(_SIR_E_PLUGINVER);
137 return 0U;
138 }
139
140 bool data_valid = true;
141
142
143 if (!_sir_validlevels(plugin->info.levels)) {
144 _sir_selflog("error: plugin (path: '%s', addr: %p) has invalid levels"
145 " %04"PRIx16, plugin->path, plugin->handle, plugin->info.levels);
146 data_valid = false;
147 }
148
149
150 if (!_sir_validopts(plugin->info.opts)) {
151 _sir_selflog("error: plugin (path: '%s', addr: %p) has invalid opts"
152 " %08"PRIx32, plugin->path, plugin->handle, plugin->info.opts);
153 data_valid = false;
154 }
155
156
157 if (!_sir_validstrnofail(plugin->info.author) ||
158 !_sir_validstrnofail(plugin->info.desc)) {
159 _sir_selflog("error: plugin (path: '%s', addr: %p) has invalid author"
160 " or description", plugin->path, plugin->handle);
161 data_valid = false;
162 }
163
164
165 if (!data_valid) {
166 _sir_plugin_destroy(&plugin);
167 (void)_sir_seterror(_SIR_E_PLUGINDAT);
168 return 0U;
169 }
170
171
172
173 if (!plugin->iface.init()) {
174 _sir_selflog("error: plugin (path: '%s', addr: %p) failed to initialize!",
175 plugin->path, plugin->handle);
176 _sir_plugin_destroy(&plugin);
177 (void)_sir_seterror(_SIR_E_PLUGINERR);
178 return 0U;
179 }
180
181 plugin->id = FNV32_1a((const uint8_t*)&plugin->iface, sizeof(sir_pluginiface));
182 plugin->valid = true;
183
184 _sir_selflog("successfully validated plugin (path: '%s', id: %08"PRIx32"); properties:"
185 SIR_EOL "{"
186 SIR_EOL "\tversion = %"PRIu8".%"PRIu8".%"PRIu8
187 SIR_EOL "\tlevels = %04"PRIx16
188 SIR_EOL "\topts = %08"PRIx32
189 SIR_EOL "\tauthor = '%s'"
190 SIR_EOL "\tdesc = '%s'"
191 SIR_EOL "\tcaps = %016"PRIx64
192 SIR_EOL "}",
193 plugin->path, plugin->id, plugin->info.maj_ver, plugin->info.min_ver,
194 plugin->info.bld_ver, plugin->info.levels, plugin->info.opts,
195 _SIR_PRNSTR(plugin->info.author), _SIR_PRNSTR(plugin->info.desc),
196 plugin->info.caps);
197
198 retval = _sir_plugin_add(plugin);
199 if (0U == retval) {
200 _sir_selflog("error: failed to add plugin (path: '%s', addr: %p) to"
201 " cache; unloading", plugin->path, plugin->handle);
202 _sir_plugin_destroy(&plugin);
203 }
204 }
205
206 return retval;
207 #else
208 SIR_UNUSED(plugin);
209 return 0U;
210 #endif
211 }
212
213 sir_pluginexport _sir_plugin_getexport(sir_pluginhandle handle, const char* name) {
214 #if !defined(SIR_NO_PLUGINS)
215 if (!_sir_validptr(handle) || !_sir_validstr(name))
216 return NULL;
217
218 # if !defined(__WIN__)
219 sir_pluginexport addr = NULL;
220 *(void**)(&addr) = dlsym(handle, name);
221
222 if (!addr) {
223 const char* err = dlerror();
224 _sir_selflog("error: dlsym(%p, '%s') failed (%s)", handle, name,
225 _SIR_PRNSTR(err));
226 (void)_sir_handleerr(errno);
227 return NULL;
228 }
229 # else
230 sir_pluginexport addr = GetProcAddress(handle, name);
231 if (!addr) {
232 DWORD err = GetLastError();
233 _sir_selflog("error: GetProcAddress(%p, '%s') failed (%lu)", handle,
234 name, err);
235 (void)_sir_handlewin32err(err);
236 return NULL;
237 }
238 # endif
239 _sir_selflog("successfully resolved plugin export (name: '%s', addr: %"
240 PRIxPTR")", name, (uintptr_t)addr);
241 return addr;
242 #else
243 SIR_UNUSED(handle);
244 SIR_UNUSED(name);
245 return NULL;
246 #endif
247 }
248
249 bool _sir_plugin_unload(sir_plugin* plugin) {
250 #if !defined(SIR_NO_PLUGINS)
251 if (!_sir_validptrnofail(plugin) || !_sir_validptrnofail(plugin->handle)) {
252 _sir_selflog("error: plugin object (%p) or handle (%p) are null;"
253 " cannot unload!", (void*)plugin, (plugin ? plugin->handle : NULL));
254 return false;
255 }
256
257
258 if (plugin->iface.cleanup && !plugin->iface.cleanup())
259 _sir_selflog("warning: plugin (path: '%s', addr: %p) reports unsuccessful"
260 " cleanup!", plugin->path, plugin->handle);
261
262 # if !defined(__WIN__)
263 if (0 != dlclose(plugin->handle)) {
264 const char* err = dlerror();
265 _sir_selflog("error: dlclose(%p) failed (%s)", plugin->handle, _SIR_PRNSTR(err));
266 return _sir_handleerr(errno);
267 }
268 # else
269 if (!FreeLibrary(plugin->handle)) {
270 DWORD err = GetLastError();
271 _sir_selflog("error: FreeLibrary(%p) failed (%lu)", plugin->handle, err);
272 return _sir_handlewin32err(err);
273 }
274 # endif
275
276 plugin->handle = NULL;
277 plugin->loaded = false;
278 _sir_selflog("unloaded plugin (path: '%s', id: %08"PRIx32")", plugin->path,
279 plugin->id);
280 return true;
281 #else
282 SIR_UNUSED(plugin);
283 return false;
284 #endif
285 }
286
287 sirpluginid _sir_plugin_add(sir_plugin* plugin) {
288 #if !defined(SIR_NO_PLUGINS)
289 sirpluginid retval = 0U;
290
291 if (_sir_validptr(plugin)) {
292 _SIR_LOCK_SECTION(sir_plugincache, spc, SIRMI_PLUGINCACHE, 0U);
293 retval = _sir_plugin_cache_add(spc, plugin);
294 _SIR_UNLOCK_SECTION(SIRMI_PLUGINCACHE);
295 }
296
297 return retval;
298 #else
299 SIR_UNUSED(plugin);
300 return 0U;
301 #endif
302 }
303
304 bool _sir_plugin_rem(sirpluginid id) {
305 #if !defined(SIR_NO_PLUGINS)
306 (void)_sir_seterror(_SIR_E_NOERROR);
307
308 if (!_sir_sanity())
309 return false;
310
311 _SIR_LOCK_SECTION(sir_plugincache, spc, SIRMI_PLUGINCACHE, false);
312 bool retval = _sir_plugin_cache_rem(spc, id);
313 _SIR_UNLOCK_SECTION(SIRMI_PLUGINCACHE);
314
315 return retval;
316 #else
317 SIR_UNUSED(id);
318 return false;
319 #endif
320 }
321
322 void _sir_plugin_destroy(sir_plugin** plugin) {
323 #if !defined(SIR_NO_PLUGINS)
324 if (_sir_validptrptr(plugin) && _sir_validptr(*plugin)) {
325 bool unloaded = _sir_plugin_unload(*plugin);
326 SIR_ASSERT_UNUSED(unloaded, unloaded);
327
328 _sir_safefree(&(*plugin)->path);
329 _sir_safefree(plugin);
330 }
331 #else
332 SIR_UNUSED(plugin);
333 #endif
334 }
335
336 bool _sir_plugin_cache_pred_id(const void* match, const sir_plugin* iter) {
337 #if !defined(SIR_NO_PLUGINS)
338 return iter->id == *((const sirpluginid*)match);
339 #else
340 SIR_UNUSED(match);
341 SIR_UNUSED(iter);
342 return false;
343 #endif
344 }
345
346 sirpluginid _sir_plugin_cache_add(sir_plugincache* spc, sir_plugin* plugin) {
347 #if !defined(SIR_NO_PLUGINS)
348 if (!_sir_validptr(spc) || !_sir_validptr(plugin))
349 return 0U;
350
351 if (spc->count >= SIR_MAXPLUGINS) {
352 (void)_sir_seterror(_SIR_E_NOROOM);
353 return 0U;
354 }
355
356 const sir_plugin* existing = _sir_plugin_cache_find_id(spc, plugin->id);
357 if (NULL != existing) {
358 _sir_selflog("error: already have plugin (path: '%s', id: %08"PRIx32")",
359 existing->path, plugin->id);
360 (void)_sir_seterror(_SIR_E_DUPITEM);
361 return 0U;
362 }
363
364 _sir_selflog("adding plugin (path: %s, id: %08"PRIx32"); count = %zu",
365 plugin->path, plugin->id, spc->count + 1);
366 spc->plugins[spc->count++] = plugin;
367 return plugin->id;
368 #else
369 SIR_UNUSED(spc);
370 SIR_UNUSED(plugin);
371 return 0U;
372 #endif
373 }
374
375 sir_plugin* _sir_plugin_cache_find_id(const sir_plugincache* spc, sirpluginid id) {
376 #if !defined(SIR_NO_PLUGINS)
377 return _sir_plugin_cache_find(spc, &id, &_sir_plugin_cache_pred_id);
378 #else
379 SIR_UNUSED(spc);
380 SIR_UNUSED(id);
381 return NULL;
382 #endif
383 }
384
385 sir_plugin* _sir_plugin_cache_find(const sir_plugincache* spc, const void* match,
386 sir_plugin_pred pred) {
387 #if !defined(SIR_NO_PLUGINS)
388 if (!_sir_validptr(spc) || !_sir_validptr(match) || !_sir_validfnptr(pred))
389 return NULL;
390
391 for (size_t n = 0; n < spc->count; n++) {
392 if (pred(match, spc->plugins[n]))
393 return spc->plugins[n];
394 }
395
396 return NULL;
397 #else
398 SIR_UNUSED(spc);
399 SIR_UNUSED(match);
400 SIR_UNUSED(pred);
401 return NULL;
402 #endif
403 }
404
405 bool _sir_plugin_cache_rem(sir_plugincache* spc, sirpluginid id) {
406 #if !defined(SIR_NO_PLUGINS)
407 if (!_sir_validptr(spc))
408 return false;
409
410 for (size_t n = 0; n < spc->count; n++) {
411 if (spc->plugins[n]->id == id) {
412 _sir_selflog("removing plugin (path: '%s', id: %"PRIx32"); count = %zu",
413 spc->plugins[n]->path, spc->plugins[n]->id, spc->count - 1);
414
415 _sir_plugin_destroy(&spc->plugins[n]);
416
417 for (size_t i = n; i < spc->count - 1; i++) {
418 spc->plugins[i] = spc->plugins[i + 1];
419 spc->plugins[i + 1] = NULL;
420 }
421
422 spc->count--;
423 return true;
424 }
425 }
426
427 return _sir_seterror(_SIR_E_NOITEM);
428 #else
429 SIR_UNUSED(spc);
430 SIR_UNUSED(id);
431 return false;
432 #endif
433 }
434
435 bool _sir_plugin_cache_destroy(sir_plugincache* spc) {
436 #if !defined(SIR_NO_PLUGINS)
437 if (!_sir_validptr(spc))
438 return false;
439
440 while (spc->count > 0) {
441 size_t idx = spc->count - 1;
442 _sir_plugin_destroy(&spc->plugins[idx]);
443 spc->plugins[idx] = NULL;
444 spc->count--;
445 }
446
447 (void)memset(spc, 0, sizeof(sir_plugincache));
448 return true;
449 #else
450 SIR_UNUSED(spc);
451 return false;
452 #endif
453 }
454
455 bool _sir_plugin_cache_dispatch(const sir_plugincache* spc, sir_level level, sirbuf* buf,
456 size_t* dispatched, size_t* wanted) {
457 #if !defined(SIR_NO_PLUGINS)
458 if (!_sir_validptr(spc) || !_sir_validlevel(level) || !_sir_validptr(buf) ||
459 !_sir_validptr(dispatched) || !_sir_validptr(wanted))
460 return false;
461
462 const char* wrote = NULL;
463 sir_options lastopts = 0;
464
465 *dispatched = 0;
466 *wanted = 0;
467
468 for (size_t n = 0; n < spc->count; n++) {
469 if (!_sir_bittest(spc->plugins[n]->info.levels, level)) {
470 _sir_selflog("level %04"PRIx16" not set in level mask (%04"PRIx16
471 ") for plugin (path: '%s', id: %08"PRIx32"); skipping",
472 level, spc->plugins[n]->info.levels, spc->plugins[n]->path,
473 spc->plugins[n]->id);
474 continue;
475 }
476
477 (*wanted)++;
478
479 if (!wrote || spc->plugins[n]->info.opts != lastopts) {
480 wrote = _sir_format(false, spc->plugins[n]->info.opts, buf);
481 SIR_ASSERT(wrote);
482 lastopts = spc->plugins[n]->info.opts;
483 }
484
485 if (wrote && spc->plugins[n]->iface.write(level, wrote)) {
486 (*dispatched)++;
487 } else {
488 _sir_selflog("error: write to plugin (path: '%s', id: %08"PRIx32")"
489 " failed!", spc->plugins[n]->path, spc->plugins[n]->id);
490 }
491 }
492
493 return (*dispatched == *wanted);
494 #else
495 SIR_UNUSED(spc);
496 SIR_UNUSED(level);
497 SIR_UNUSED(buf);
498 SIR_UNUSED(dispatched);
499 SIR_UNUSED(wanted);
500 return false;
501 #endif
502 }