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

aremmell / libsir / 1211

31 Oct 2023 01:44PM UTC coverage: 95.477%. Remained the same
1211

Pull #346

gitlab-ci

johnsonjh
Regenerate docs

Signed-off-by: Jeffrey H. Johnson <trnsz@pobox.com>
Pull Request #346: Stricter SPDX compliance

3863 of 4046 relevant lines covered (95.48%)

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

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

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

40
    if (!_sir_sanity() || !_sir_validstr(path))
220✔
41
        return 0U;
22✔
42

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

47
# if !defined (__WIN__)
48
    plugin->handle = dlopen(path, RTLD_NOW | RTLD_LOCAL);
189✔
49
    if (!plugin->handle) {
189✔
50
        const char* err = dlerror();
29✔
51
        _sir_selflog("error: dlopen('%s') failed (%s)", path, _SIR_PRNSTR(err));
28✔
52
        _sir_plugin_destroy(&plugin);
29✔
53
        return _sir_handleerr(errno);
29✔
54
    }
55
# else /* __WIN__ */
56
    UINT old_error_mode = SetErrorMode(SEM_FAILCRITICALERRORS);
57
    plugin->handle      = LoadLibraryA(path);
58
    SetErrorMode(old_error_mode);
59
    if (!plugin->handle) {
60
        DWORD err = GetLastError();
61
        _sir_selflog("error: LoadLibraryA(%s) failed (%lu)", path, err);
62
        _sir_plugin_destroy(&plugin);
63
        return _sir_handlewin32err(err);
64
    }
65
# endif
66

67
    plugin->loaded = true;
160✔
68
    plugin->path   = strndup(path, strnlen(path, SIR_MAXPATH));
160✔
69

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

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

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

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

108
        if (!plugin->iface.query || !plugin->iface.init ||
152✔
109
            !plugin->iface.write || !plugin->iface.cleanup) {
126✔
110
            _sir_selflog("error: export(s) not resolved for plugin (path:"
25✔
111
                         " '%s', addr: %p)!", plugin->path, plugin->handle);
112
            _sir_selflog("exports (query: %"PRIxPTR", init: %"PRIxPTR", write:"
25✔
113
                         " %"PRIxPTR", cleanup; %"PRIxPTR")",
114
                         (uintptr_t)plugin->iface.query, (uintptr_t)plugin->iface.init,
115
                         (uintptr_t)plugin->iface.write, (uintptr_t)plugin->iface.cleanup);
116
            _sir_plugin_destroy(&plugin);
26✔
117
            return _sir_seterror(_SIR_E_PLUGINBAD);
26✔
118
        }
119
# else
120
#  error "plugin version not implemented"
121
# endif
122
        /* query the plugin for information. */
123
        if (!plugin->iface.query(&plugin->info)) {
126✔
124
            _sir_selflog("error: plugin (path: '%s', addr: %p) returned false from"
17✔
125
                         " query fn!", plugin->path, plugin->handle);
126
            _sir_plugin_destroy(&plugin);
18✔
127
            (void)_sir_seterror(_SIR_E_PLUGINERR);
18✔
128
            return 0U;
18✔
129
        }
130

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

141
        bool data_valid = true;
75✔
142

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

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

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

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

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

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

185
        _sir_selflog("successfully validated plugin (path: '%s', id: %08"PRIx32");"
63✔
186
                     " properties:\n{\n\tversion = %"PRIu8".%"PRIu8".%"PRIu8"\n\t"
187
                     "levels = %04"PRIx16"\n\topts = %08"PRIx32"\n\tauthor = '%s'"
188
                     "\n\tdesc = '%s'\n\tcaps = %016"PRIx64"\n}", plugin->path,
189
                     plugin->id, plugin->info.maj_ver, plugin->info.min_ver,
190
                     plugin->info.bld_ver, plugin->info.levels, plugin->info.opts,
191
                     _SIR_PRNSTR(plugin->info.author), _SIR_PRNSTR(plugin->info.desc),
192
                     plugin->info.caps);
193

194
        retval = _sir_plugin_add(plugin);
54✔
195
        if (0U == retval) {
54✔
196
            _sir_selflog("error: failed to add plugin (path: '%s', addr: %p) to"
17✔
197
                         " cache; unloading", plugin->path, plugin->handle);
198
            _sir_plugin_destroy(&plugin);
18✔
199
        }
200
    }
201

202
    return retval;
45✔
203
#else
204
    SIR_UNUSED(plugin);
205
    return 0U;
×
206
#endif
207
}
208

209
sir_pluginexport _sir_plugin_getexport(sir_pluginhandle handle, const char* name) {
608✔
210
#if !defined(SIR_NO_PLUGINS)
211
    if (!_sir_validptr(handle) || !_sir_validstr(name))
608✔
212
        return NULL;
×
213

214
# if !defined(__WIN__)
215
    sir_pluginexport addr = NULL;
512✔
216
    *(void**)(&addr) = dlsym(handle, name);
608✔
217

218
    if (!addr) {
608✔
219
        const char* err = dlerror();
50✔
220
        _sir_selflog("error: dlsym(%p, '%s') failed (%s)", handle, name,
49✔
221
            _SIR_PRNSTR(err));
222
        (void)_sir_handleerr(errno);
50✔
223
        return NULL;
50✔
224
    }
225
# else /* __WIN__ */
226
    sir_pluginexport addr = GetProcAddress(handle, name);
227
    if (!addr) {
228
        DWORD err = GetLastError();
229
        _sir_selflog("error: GetProcAddress(%p, '%s') failed (%lu)", handle,
230
            name, err);
231
        (void)_sir_handlewin32err(err);
232
        return NULL;
233
    }
234
# endif
235
    _sir_selflog("successfully resolved plugin export (name: '%s', addr: %"
527✔
236
                 PRIxPTR")", name, (uintptr_t)addr);
237
    return addr;
527✔
238
#else
239
    SIR_UNUSED(handle);
240
    SIR_UNUSED(name);
241
    return NULL;
×
242
#endif
243
}
244

245
bool _sir_plugin_unload(sir_plugin* plugin) {
189✔
246
#if !defined(SIR_NO_PLUGINS)
247
    if (!_sir_validptrnofail(plugin) || !_sir_validptrnofail(plugin->handle)) {
189✔
248
        _sir_selflog("error: plugin object (%p) or handle (%p) are null;"
28✔
249
                     " cannot unload!", (void*)plugin, (plugin ? plugin->handle : NULL));
250
        return false;
29✔
251
    }
252

253
    /* if the plugin cleanup export was resolved, call it. */
254
    if (plugin->iface.cleanup && !plugin->iface.cleanup())
160✔
255
        _sir_selflog("warning: plugin (path: '%s', addr: %p) reports unsuccessful"
17✔
256
                     " cleanup!", plugin->path, plugin->handle);
257

258
# if !defined(__WIN__)
259
    if (0 != dlclose(plugin->handle)) {
160✔
260
        const char* err = dlerror();
×
261
        _sir_selflog("error: dlclose(%p) failed (%s)", plugin->handle, _SIR_PRNSTR(err));
×
262
        return _sir_handleerr(errno);
×
263
    }
264
# else /* __WIN__ */
265
    if (!FreeLibrary(plugin->handle)) {
266
        DWORD err = GetLastError();
267
        _sir_selflog("error: FreeLibrary(%p) failed (%lu)", plugin->handle, err);
268
        return _sir_handlewin32err(err);
269
    }
270
# endif
271

272
    plugin->handle = NULL;
160✔
273
    plugin->loaded = false;
160✔
274
    _sir_selflog("unloaded plugin (path: '%s', id: %08"PRIx32")", plugin->path,
152✔
275
        plugin->id);
276
    return true;
160✔
277
#else
278
    SIR_UNUSED(plugin);
279
    return false;
×
280
#endif
281
}
282

283
sirpluginid _sir_plugin_add(sir_plugin* plugin) {
54✔
284
#if !defined(SIR_NO_PLUGINS)
285
    sirpluginid retval = 0U;
45✔
286

287
    if (_sir_validptr(plugin)) {
54✔
288
        _SIR_LOCK_SECTION(sir_plugincache, spc, SIRMI_PLUGINCACHE, 0U);
54✔
289
        retval = _sir_plugin_cache_add(spc, plugin);
54✔
290
        _SIR_UNLOCK_SECTION(SIRMI_PLUGINCACHE);
54✔
291
    }
292

293
    return retval;
45✔
294
#else
295
    SIR_UNUSED(plugin);
296
    return 0U;
×
297
#endif
298
}
299

300
bool _sir_plugin_rem(sirpluginid id) {
44✔
301
#if !defined(SIR_NO_PLUGINS)
302
    (void)_sir_seterror(_SIR_E_NOERROR);
44✔
303

304
    if (!_sir_sanity())
44✔
305
        return false;
×
306

307
    _SIR_LOCK_SECTION(sir_plugincache, spc, SIRMI_PLUGINCACHE, false);
44✔
308
    bool retval = _sir_plugin_cache_rem(spc, id);
44✔
309
    _SIR_UNLOCK_SECTION(SIRMI_PLUGINCACHE);
44✔
310

311
    return retval;
44✔
312
#else
313
    SIR_UNUSED(id);
314
    return false;
×
315
#endif
316
}
317

318
void _sir_plugin_destroy(sir_plugin** plugin) {
189✔
319
#if !defined(SIR_NO_PLUGINS)
320
    if (_sir_validptrptr(plugin) && _sir_validptr(*plugin)) {
189✔
321
        bool unloaded = _sir_plugin_unload(*plugin);
189✔
322
        SIR_ASSERT_UNUSED(unloaded, unloaded);
180✔
323

324
        _sir_safefree(&(*plugin)->path);
189✔
325
        _sir_safefree(plugin);
189✔
326
    }
327
#else
328
    SIR_UNUSED(plugin);
329
#endif
330
}
189✔
331

332
bool _sir_plugin_cache_pred_id(const void* match, const sir_plugin* iter) {
36✔
333
#if !defined(SIR_NO_PLUGINS)
334
    return iter->id == *((const sirpluginid*)match);
36✔
335
#else
336
    SIR_UNUSED(match);
337
    SIR_UNUSED(iter);
338
    return false;
×
339
#endif
340
}
341

342
sirpluginid _sir_plugin_cache_add(sir_plugincache* spc, sir_plugin* plugin) {
54✔
343
#if !defined(SIR_NO_PLUGINS)
344
    if (!_sir_validptr(spc) || !_sir_validptr(plugin))
54✔
345
        return 0U;
×
346

347
    if (spc->count >= SIR_MAXPLUGINS) {
54✔
348
        (void)_sir_seterror(_SIR_E_NOROOM);
×
349
        return 0U;
×
350
    }
351

352
    const sir_plugin* existing = _sir_plugin_cache_find_id(spc, plugin->id);
54✔
353
    if (NULL != existing) {
54✔
354
        _sir_selflog("error: already have plugin (path: '%s', id: %08"PRIx32")",
17✔
355
            existing->path, plugin->id);
356
        (void)_sir_seterror(_SIR_E_DUPITEM);
18✔
357
        return 0U;
18✔
358
    }
359

360
    _sir_selflog("adding plugin (path: %s, id: %08"PRIx32"); count = %zu",
36✔
361
    plugin->path, plugin->id, spc->count + 1);
362
    spc->plugins[spc->count++] = plugin;
36✔
363
    return plugin->id;
36✔
364
#else
365
    SIR_UNUSED(spc);
366
    SIR_UNUSED(plugin);
367
    return 0U;
×
368
#endif
369
}
370

371
sir_plugin* _sir_plugin_cache_find_id(const sir_plugincache* spc, sirpluginid id) {
54✔
372
#if !defined(SIR_NO_PLUGINS)
373
    return _sir_plugin_cache_find(spc, &id, &_sir_plugin_cache_pred_id);
54✔
374
#else
375
    SIR_UNUSED(spc);
376
    SIR_UNUSED(id);
377
    return NULL;
×
378
#endif
379
}
380

381
sir_plugin* _sir_plugin_cache_find(const sir_plugincache* spc, const void* match,
54✔
382
    sir_plugin_pred pred) {
383
#if !defined(SIR_NO_PLUGINS)
384
    if (!_sir_validptr(spc) || !_sir_validptr(match) || !_sir_validfnptr(pred))
54✔
385
        return NULL;
×
386

387
    for (size_t n = 0; n < spc->count; n++) {
72✔
388
        if (pred(match, spc->plugins[n]))
36✔
389
            return spc->plugins[n];
18✔
390
    }
391

392
    return NULL;
30✔
393
#else
394
    SIR_UNUSED(spc);
395
    SIR_UNUSED(match);
396
    SIR_UNUSED(pred);
397
    return NULL;
×
398
#endif
399
}
400

401
bool _sir_plugin_cache_rem(sir_plugincache* spc, sirpluginid id) {
44✔
402
#if !defined(SIR_NO_PLUGINS)
403
    if (!_sir_validptr(spc))
44✔
404
        return false;
×
405

406
    for (size_t n = 0; n < spc->count; n++) {
62✔
407
        if (spc->plugins[n]->id == id) {
36✔
408
            _sir_selflog("removing plugin (path: '%s', id: %"PRIx32"); count = %zu",
17✔
409
                spc->plugins[n]->path, spc->plugins[n]->id, spc->count - 1);
410

411
            _sir_plugin_destroy(&spc->plugins[n]);
18✔
412

413
            for (size_t i = n; i < spc->count - 1; i++) {
36✔
414
                spc->plugins[i] = spc->plugins[i + 1];
18✔
415
                spc->plugins[i + 1] = NULL;
18✔
416
            }
417

418
            spc->count--;
18✔
419
            return true;
18✔
420
        }
421
    }
422

423
    return _sir_seterror(_SIR_E_NOITEM);
26✔
424
#else
425
    SIR_UNUSED(spc);
426
    SIR_UNUSED(id);
427
    return false;
×
428
#endif
429
}
430

431
bool _sir_plugin_cache_destroy(sir_plugincache* spc) {
843✔
432
#if !defined(SIR_NO_PLUGINS)
433
    if (!_sir_validptr(spc))
843✔
434
        return false;
×
435

436
    while (spc->count > 0) {
861✔
437
        size_t idx = spc->count - 1;
18✔
438
        _sir_plugin_destroy(&spc->plugins[idx]);
18✔
439
        spc->plugins[idx] = NULL;
18✔
440
        spc->count--;
18✔
441
    }
442

443
    (void)memset(spc, 0, sizeof(sir_plugincache));
725✔
444
    return true;
843✔
445
#else
446
    SIR_UNUSED(spc);
447
    return false;
×
448
#endif
449
}
450

451
bool _sir_plugin_cache_dispatch(const sir_plugincache* spc, sir_level level, sirbuf* buf,
129,412✔
452
    size_t* dispatched, size_t* wanted) {
453
#if !defined(SIR_NO_PLUGINS)
454
    if (!_sir_validptr(spc) || !_sir_validlevel(level) || !_sir_validptr(buf) ||
149,492✔
455
        !_sir_validptr(dispatched) || !_sir_validptr(wanted))
149,492✔
456
        return false;
×
457

458
    const char* write    = NULL;
109,332✔
459
    sir_options lastopts = 0;
109,332✔
460

461
    *dispatched = 0;
129,412✔
462
    *wanted     = 0;
129,412✔
463

464
    for (size_t n = 0; n < spc->count; n++) {
129,484✔
465
        if (!_sir_bittest(spc->plugins[n]->info.levels, level)) {
72✔
466
            _sir_selflog("level %04"PRIx16" not set in level mask (%04"PRIx16
17✔
467
                         ") for plugin (path: '%s', id: %08"PRIx32"); skipping",
468
                         level, spc->plugins[n]->info.levels, spc->plugins[n]->path,
469
                         spc->plugins[n]->id);
470
            continue;
18✔
471
        }
472

473
        (*wanted)++;
54✔
474

475
        if (!write || spc->plugins[n]->info.opts != lastopts) {
54✔
476
            write = _sir_format(false, spc->plugins[n]->info.opts, buf);
36✔
477
            SIR_ASSERT(write);
34✔
478
            lastopts = spc->plugins[n]->info.opts;
36✔
479
        }
480

481
        if (write && spc->plugins[n]->iface.write(level, write)) {
54✔
482
            (*dispatched)++;
36✔
483
        } else {
484
            _sir_selflog("error: write to plugin (path: '%s', id: %08"PRIx32")"
17✔
485
                         " failed!", spc->plugins[n]->path, spc->plugins[n]->id);
486
        }
487
    }
488

489
    return (*dispatched == *wanted);
129,412✔
490
#else
491
    SIR_UNUSED(spc);
492
    SIR_UNUSED(level);
493
    SIR_UNUSED(buf);
494
    SIR_UNUSED(dispatched);
495
    SIR_UNUSED(wanted);
496
    return false;
×
497
#endif
498
}
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

© 2026 Coveralls, Inc