• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

aremmell / libsir / 1893

27 Mar 2025 08:58AM UTC coverage: 95.499%. Remained the same
1893

Pull #452

gitlab-ci

johnsonjh
Sync mcmb with upstream

Signed-off-by: Jeffrey H. Johnson <trnsz@pobox.com>
Pull Request #452: CI overhaul; update Doxygen

3692 of 3866 relevant lines covered (95.5%)

355182.55 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

87.8
/src/sirplugins.c
1
/*
2
 * sirplugins.c
3
 *
4
 * Version: 2.2.6
5
 *
6
 * -----------------------------------------------------------------------------
7
 *
8
 * SPDX-License-Identifier: MIT
9
 *
10
 * Copyright (c) 2018-2024 Ryan M. Lederman <lederman@gmail.com>
11
 *
12
 * Permission is hereby granted, free of charge, to any person obtaining a copy of
13
 * this software and associated documentation files (the "Software"), to deal in
14
 * the Software without restriction, including without limitation the rights to
15
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
16
 * the Software, and to permit persons to whom the Software is furnished to do so,
17
 * subject to the following conditions:
18
 *
19
 * The above copyright notice and this permission notice shall be included in all
20
 * copies or substantial portions of the Software.
21
 *
22
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
24
 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
25
 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
26
 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
27
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
28
 *
29
 * -----------------------------------------------------------------------------
30
 */
31

32
#include "sir/plugins.h"
33
#include "sir/internal.h"
34

35
sirpluginid _sir_plugin_load(const char* path) {
260✔
36
#if !defined(SIR_NO_PLUGINS)
37
    (void)_sir_seterror(_SIR_E_NOERROR);
260✔
38

39
    if (!_sir_sanity() || !_sir_validstr(path))
260✔
40
        return 0U;
26✔
41

42
    sir_plugin* plugin = (sir_plugin*)calloc(1, sizeof(sir_plugin));
234✔
43
    if (!plugin)
234✔
44
        return _sir_handleerr(errno);
9✔
45

46
# if !defined (__WIN__)
47
    plugin->handle = dlopen(path, RTLD_NOW | RTLD_LOCAL);
225✔
48
    if (!plugin->handle) {
225✔
49
        const char* err = dlerror();
33✔
50
        _sir_selflog("error: dlopen('%s') failed (%s)", path, _SIR_PRNSTR(err));
31✔
51
        _sir_plugin_destroy(&plugin);
33✔
52
        return _sir_handleerr(errno);
33✔
53
    }
54
# else /* __WIN__ */
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;
192✔
67
    plugin->path   = strndup(path, strnlen(path, SIR_MAXPATH));
192✔
68

69
    if (!_sir_validstrnofail(plugin->path)) {
192✔
70
        _sir_plugin_destroy(&plugin);
8✔
71
        return _sir_handleerr(errno);
8✔
72
    }
73

74
    _sir_selflog("loaded plugin (path: '%s', addr: %p); probing...", //-V576
184✔
75
        plugin->path, plugin->handle);
76

77
    return _sir_plugin_probe(plugin);
184✔
78
#else
79
    SIR_UNUSED(path);
80
    return 0U;
81
#endif
82
}
83

84
sirpluginid _sir_plugin_probe(sir_plugin* plugin) {
184✔
85
#if !defined(SIR_NO_PLUGINS)
86
    sirpluginid retval = 0U;
152✔
87
    if (plugin) {
184✔
88
# if SIR_PLUGIN_VCURRENT == SIR_PLUGIN_V1
89
       /* if/when new versions of plugin interfaces are introduced, we will need to
90
        * modify/extend the following code:
91
        *
92
        * - remove the enclosing #if
93
        * - get the v1 exports (all versions will have v1 exports), resolve them,
94
        * and call sir_plugin_query.
95
        * - switch on version returned to resolve additional exports. this will
96
        * necessitate additional versioned interface structures as members of the
97
        * sir_plugin struct, e.g. ifacev1, ifacev2). */
98
        plugin->iface.query   = (sir_plugin_queryfn)
336✔
99
            _sir_plugin_getexport(plugin->handle, SIR_PLUGIN_EXPORT_QUERY);
184✔
100
        plugin->iface.init    = (sir_plugin_initfn)
336✔
101
            _sir_plugin_getexport(plugin->handle, SIR_PLUGIN_EXPORT_INIT);
184✔
102
        plugin->iface.write   = (sir_plugin_writefn)
336✔
103
            _sir_plugin_getexport(plugin->handle, SIR_PLUGIN_EXPORT_WRITE);
184✔
104
        plugin->iface.cleanup = (sir_plugin_cleanupfn)
336✔
105
            _sir_plugin_getexport(plugin->handle, SIR_PLUGIN_EXPORT_CLEANUP);
184✔
106

107
        if (!plugin->iface.query || !plugin->iface.init ||
184✔
108
            !plugin->iface.write || !plugin->iface.cleanup) {
154✔
109
            _sir_selflog("error: export(s) not resolved for plugin (path:"
28✔
110
                         " '%s', addr: %p)!", plugin->path, plugin->handle);
111
            _sir_selflog("exports (query: %"PRIxPTR", init: %"PRIxPTR", write:"
28✔
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);
30✔
116
            return _sir_seterror(_SIR_E_PLUGINBAD);
30✔
117
        }
118
# else
119
#  error "plugin version not implemented"
120
# endif
121
        /* query the plugin for information. */
122
        if (!plugin->iface.query(&plugin->info)) {
154✔
123
            _sir_selflog("error: plugin (path: '%s', addr: %p) returned false from"
20✔
124
                         " query fn!", plugin->path, plugin->handle);
125
            _sir_plugin_destroy(&plugin);
22✔
126
            (void)_sir_seterror(_SIR_E_PLUGINERR);
22✔
127
            return 0U;
22✔
128
        }
129

130
        /* verify version. */
131
        if (!plugin->info.iface_ver || plugin->info.iface_ver > SIR_PLUGIN_VCURRENT) {
132✔
132
            _sir_selflog("error: plugin (path: '%s', addr: %p) has version"
20✔
133
                         " %"PRIu8"; libsir has %d", plugin->path, plugin->handle,
134
                        plugin->info.iface_ver, SIR_PLUGIN_VCURRENT);
135
            _sir_plugin_destroy(&plugin);
22✔
136
            (void)_sir_seterror(_SIR_E_PLUGINVER);
22✔
137
            return 0U;
22✔
138
        }
139

140
        bool data_valid = true;
90✔
141

142
        /* verify level registration bitmask. */
143
        if (!_sir_validlevels(plugin->info.levels)) {
110✔
144
            _sir_selflog("error: plugin (path: '%s', addr: %p) has invalid levels"
20✔
145
                         " %04"PRIx16, plugin->path, plugin->handle, plugin->info.levels);
146
            data_valid = false;
18✔
147
        }
148

149
        /* verify formatting options bitmask. */
150
        if (!_sir_validopts(plugin->info.opts)) {
110✔
151
            _sir_selflog("error: plugin (path: '%s', addr: %p) has invalid opts"
20✔
152
                         " %08"PRIx32, plugin->path, plugin->handle, plugin->info.opts);
153
            data_valid = false;
18✔
154
        }
155

156
        /* verify strings */
157
        if (!_sir_validstrnofail(plugin->info.author) ||
126✔
158
            !_sir_validstrnofail(plugin->info.desc)) {
88✔
159
            _sir_selflog("error: plugin (path: '%s', addr: %p) has invalid author"
20✔
160
                         " or description", plugin->path, plugin->handle);
161
            data_valid = false;
18✔
162
        }
163

164
        /* if any category of data is invalid, fail and unload. */
165
        if (!data_valid) {
106✔
166
            _sir_plugin_destroy(&plugin);
22✔
167
            (void)_sir_seterror(_SIR_E_PLUGINDAT);
22✔
168
            return 0U;
22✔
169
        }
170

171
        /* plugin is valid; tell it to initialize, assign it an id,
172
         * print its information, and add to cache. */
173
        if (!plugin->iface.init()) {
88✔
174
            _sir_selflog("error: plugin (path: '%s', addr: %p) failed to initialize!",
20✔
175
                plugin->path, plugin->handle);
176
            _sir_plugin_destroy(&plugin);
22✔
177
            (void)_sir_seterror(_SIR_E_PLUGINERR);
22✔
178
            return 0U;
22✔
179
        }
180

181
        plugin->id    = FNV32_1a((const uint8_t*)&plugin->iface, sizeof(sir_pluginiface));
66✔
182
        plugin->valid = true;
66✔
183

184
        _sir_selflog("successfully validated plugin (path: '%s', id: %08"PRIx32"); properties:"
72✔
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);
66✔
199
        if (0U == retval) {
66✔
200
            _sir_selflog("error: failed to add plugin (path: '%s', addr: %p) to"
20✔
201
                         " cache; unloading", plugin->path, plugin->handle);
202
            _sir_plugin_destroy(&plugin);
22✔
203
        }
204
    }
205

206
    return retval;
54✔
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) {
736✔
214
#if !defined(SIR_NO_PLUGINS)
215
    if (!_sir_validptr(handle) || !_sir_validstr(name))
736✔
216
        return NULL;
×
217

218
# if !defined(__WIN__)
219
    sir_pluginexport addr = NULL;
608✔
220
    *(void**)(&addr) = dlsym(handle, name);
736✔
221

222
    if (!addr) {
736✔
223
        const char* err = dlerror();
54✔
224
        _sir_selflog("error: dlsym(%p, '%s') failed (%s)", handle, name,
52✔
225
            _SIR_PRNSTR(err));
226
        (void)_sir_handleerr(errno);
54✔
227
        return NULL;
54✔
228
    }
229
# else /* __WIN__ */
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: %"
620✔
240
                 PRIxPTR")", name, (uintptr_t)addr);
241
    return addr;
620✔
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) {
225✔
250
#if !defined(SIR_NO_PLUGINS)
251
    if (!_sir_validptrnofail(plugin) || !_sir_validptrnofail(plugin->handle)) {
225✔
252
        _sir_selflog("error: plugin object (%p) or handle (%p) are null;"
31✔
253
                     " cannot unload!", (void*)plugin, (plugin ? plugin->handle : NULL));
254
        return false;
33✔
255
    }
256

257
    /* if the plugin cleanup export was resolved, call it. */
258
    if (plugin->iface.cleanup && !plugin->iface.cleanup())
192✔
259
        _sir_selflog("warning: plugin (path: '%s', addr: %p) reports unsuccessful"
20✔
260
                     " cleanup!", plugin->path, plugin->handle);
261

262
# if !defined(__WIN__)
263
    if (0 != dlclose(plugin->handle)) {
192✔
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 /* __WIN__ */
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;
192✔
277
    plugin->loaded = false;
192✔
278
    _sir_selflog("unloaded plugin (path: '%s', id: %08"PRIx32")", plugin->path,
176✔
279
        plugin->id);
280
    return true;
192✔
281
#else
282
    SIR_UNUSED(plugin);
283
    return false;
284
#endif
285
}
286

287
sirpluginid _sir_plugin_add(sir_plugin* plugin) {
66✔
288
#if !defined(SIR_NO_PLUGINS)
289
    sirpluginid retval = 0U;
54✔
290

291
    if (_sir_validptr(plugin)) {
66✔
292
        _SIR_LOCK_SECTION(sir_plugincache, spc, SIRMI_PLUGINCACHE, 0U);
66✔
293
        retval = _sir_plugin_cache_add(spc, plugin);
66✔
294
        _SIR_UNLOCK_SECTION(SIRMI_PLUGINCACHE);
66✔
295
    }
296

297
    return retval;
54✔
298
#else
299
    SIR_UNUSED(plugin);
300
    return 0U;
301
#endif
302
}
303

304
bool _sir_plugin_rem(sirpluginid id) {
52✔
305
#if !defined(SIR_NO_PLUGINS)
306
    (void)_sir_seterror(_SIR_E_NOERROR);
52✔
307

308
    if (!_sir_sanity())
52✔
309
        return false;
×
310

311
    _SIR_LOCK_SECTION(sir_plugincache, spc, SIRMI_PLUGINCACHE, false);
52✔
312
    bool retval = _sir_plugin_cache_rem(spc, id);
52✔
313
    _SIR_UNLOCK_SECTION(SIRMI_PLUGINCACHE);
52✔
314

315
    return retval;
52✔
316
#else
317
    SIR_UNUSED(id);
318
    return false;
319
#endif
320
}
321

322
void _sir_plugin_destroy(sir_plugin** plugin) {
225✔
323
#if !defined(SIR_NO_PLUGINS)
324
    if (_sir_validptrptr(plugin) && _sir_validptr(*plugin)) {
225✔
325
        bool unloaded = _sir_plugin_unload(*plugin);
225✔
326
        SIR_ASSERT_UNUSED(unloaded, unloaded);
207✔
327

328
        _sir_safefree(&(*plugin)->path);
225✔
329
        _sir_safefree(plugin);
225✔
330
    }
331
#else
332
    SIR_UNUSED(plugin);
333
#endif
334
}
225✔
335

336
bool _sir_plugin_cache_pred_id(const void* match, const sir_plugin* iter) {
44✔
337
#if !defined(SIR_NO_PLUGINS)
338
    return iter->id == *((const sirpluginid*)match);
44✔
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) {
66✔
347
#if !defined(SIR_NO_PLUGINS)
348
    if (!_sir_validptr(spc) || !_sir_validptr(plugin))
66✔
349
        return 0U;
×
350

351
    if (spc->count >= SIR_MAXPLUGINS) {
66✔
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);
66✔
357
    if (NULL != existing) {
66✔
358
        _sir_selflog("error: already have plugin (path: '%s', id: %08"PRIx32")",
20✔
359
            existing->path, plugin->id);
360
        (void)_sir_seterror(_SIR_E_DUPITEM);
22✔
361
        return 0U;
22✔
362
    }
363

364
    _sir_selflog("adding plugin (path: %s, id: %08"PRIx32"); count = %zu",
44✔
365
    plugin->path, plugin->id, spc->count + 1);
366
    spc->plugins[spc->count++] = plugin;
44✔
367
    return plugin->id;
44✔
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) {
66✔
376
#if !defined(SIR_NO_PLUGINS)
377
    return _sir_plugin_cache_find(spc, &id, &_sir_plugin_cache_pred_id);
66✔
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,
66✔
386
    sir_plugin_pred pred) {
387
#if !defined(SIR_NO_PLUGINS)
388
    if (!_sir_validptr(spc) || !_sir_validptr(match) || !_sir_validfnptr(pred))
66✔
389
        return NULL;
×
390

391
    for (size_t n = 0; n < spc->count; n++) {
88✔
392
        if (pred(match, spc->plugins[n]))
44✔
393
            return spc->plugins[n];
22✔
394
    }
395

396
    return NULL;
36✔
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) {
52✔
406
#if !defined(SIR_NO_PLUGINS)
407
    if (!_sir_validptr(spc))
52✔
408
        return false;
×
409

410
    for (size_t n = 0; n < spc->count; n++) {
74✔
411
        if (spc->plugins[n]->id == id) {
44✔
412
            _sir_selflog("removing plugin (path: '%s', id: %"PRIx32"); count = %zu",
20✔
413
                spc->plugins[n]->path, spc->plugins[n]->id, spc->count - 1);
414

415
            _sir_plugin_destroy(&spc->plugins[n]);
22✔
416

417
            for (size_t i = n; i < spc->count - 1; i++) {
44✔
418
                spc->plugins[i] = spc->plugins[i + 1];
22✔
419
                spc->plugins[i + 1] = NULL;
22✔
420
            }
421

422
            spc->count--;
22✔
423
            return true;
22✔
424
        }
425
    }
426

427
    return _sir_seterror(_SIR_E_NOITEM);
30✔
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) {
1,001✔
436
#if !defined(SIR_NO_PLUGINS)
437
    if (!_sir_validptr(spc))
1,001✔
438
        return false;
×
439

440
    while (spc->count > 0) {
1,023✔
441
        size_t idx = spc->count - 1;
22✔
442
        _sir_plugin_destroy(&spc->plugins[idx]);
22✔
443
        spc->plugins[idx] = NULL;
22✔
444
        spc->count--;
22✔
445
    }
446

447
    (void)memset(spc, 0, sizeof(sir_plugincache));
844✔
448
    return true;
1,001✔
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,
101,752✔
456
    size_t* dispatched, size_t* wanted) {
457
#if !defined(SIR_NO_PLUGINS)
458
    if (!_sir_validptr(spc) || !_sir_validlevel(level) || !_sir_validptr(buf) ||
118,374✔
459
        !_sir_validptr(dispatched) || !_sir_validptr(wanted))
118,374✔
460
        return false;
×
461

462
    const char* wrote    = NULL;
85,130✔
463
    sir_options lastopts = 0;
85,130✔
464

465
    *dispatched = 0;
101,752✔
466
    *wanted     = 0;
101,752✔
467

468
    for (size_t n = 0; n < spc->count; n++) {
101,840✔
469
        if (!_sir_bittest(spc->plugins[n]->info.levels, level)) {
88✔
470
            _sir_selflog("level %04"PRIx16" not set in level mask (%04"PRIx16
20✔
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;
22✔
475
        }
476

477
        (*wanted)++;
66✔
478

479
        if (!wrote || spc->plugins[n]->info.opts != lastopts) {
66✔
480
            wrote = _sir_format(false, spc->plugins[n]->info.opts, buf);
44✔
481
            SIR_ASSERT(wrote);
40✔
482
            lastopts = spc->plugins[n]->info.opts;
44✔
483
        }
484

485
        if (wrote && spc->plugins[n]->iface.write(level, wrote)) {
66✔
486
            (*dispatched)++;
44✔
487
        } else {
488
            _sir_selflog("error: write to plugin (path: '%s', id: %08"PRIx32")"
20✔
489
                         " failed!", spc->plugins[n]->path, spc->plugins[n]->id);
490
        }
491
    }
492

493
    return (*dispatched == *wanted);
101,752✔
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
}
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc