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

aremmell / libsir / 433

05 Sep 2023 12:02AM UTC coverage: 94.671% (-0.2%) from 94.865%
433

Pull #257

gitlab-ci

aremmell
flip ifdef logic for static init
Pull Request #257: General performance enhancements & portability improvements

236 of 236 new or added lines in 13 files covered. (100.0%)

3020 of 3190 relevant lines covered (94.67%)

632992.37 hits per line

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

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

30
sirpluginid _sir_plugin_load(const char* path) {
220✔
31
#if !defined(SIR_NO_PLUGINS)
32
    (void)_sir_seterror(_SIR_E_NOERROR);
220✔
33

34
    if (!_sir_sanity() || !_sir_validstr(path))
220✔
35
        return 0U;
22✔
36

37
    sir_plugin* plugin = (sir_plugin*)calloc(1, sizeof(sir_plugin));
198✔
38
    if (!plugin)
198✔
39
        return _sir_handleerr(errno);
9✔
40

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

61
    plugin->loaded = true;
160✔
62
    plugin->path   = strndup(path, strnlen(path, SIR_MAXPATH));
160✔
63

64
    if (!_sir_validstrnofail(plugin->path)) {
160✔
65
        _sir_plugin_destroy(&plugin);
8✔
66
        return _sir_handleerr(errno);
8✔
67
    }
68

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

72
    return _sir_plugin_probe(plugin);
152✔
73
#else
74
    SIR_UNUSED(path);
75
    return 0U;
×
76
#endif
77
}
78

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

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

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

135
        bool data_valid = true;
75✔
136

137
        /* verify level registration bitmask. */
138
        if (!_sir_validlevels(plugin->info.levels)) {
90✔
139
            _sir_selflog("error: plugin (path: '%s', addr: %p) has invalid levels"
17✔
140
                         " %04"PRIx16, plugin->path, plugin->handle, plugin->info.levels);
141
            data_valid = false;
15✔
142
        }
143

144
        /* verify formatting options bitmask. */
145
        if (!_sir_validopts(plugin->info.opts)) {
90✔
146
            _sir_selflog("error: plugin (path: '%s', addr: %p) has invalid opts"
17✔
147
                         " %08"PRIx32, plugin->path, plugin->handle, plugin->info.opts);
148
            data_valid = false;
15✔
149
        }
150

151
        /* verify strings */
152
        if (!_sir_validstrnofail(plugin->info.author) ||
102✔
153
            !_sir_validstrnofail(plugin->info.desc)) {
72✔
154
            _sir_selflog("error: plugin (path: '%s', addr: %p) has invalid author"
17✔
155
                         " or description", plugin->path, plugin->handle);
156
            data_valid = false;
15✔
157
        }
158

159
        /* if any category of data is invalid, fail and unload. */
160
        if (!data_valid) {
87✔
161
            _sir_plugin_destroy(&plugin);
18✔
162
            (void)_sir_seterror(_SIR_E_PLUGINDAT);
18✔
163
            return 0U;
18✔
164
        }
165

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

176
        plugin->id    = FNV32_1a((const uint8_t*)&plugin->iface, sizeof(sir_pluginiface));
54✔
177
        plugin->valid = true;
54✔
178

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

188
        retval = _sir_plugin_add(plugin);
54✔
189
        if (0U == retval) {
54✔
190
            _sir_selflog("error: failed to add plugin (path: '%s', addr: %p) to"
17✔
191
                         " cache; unloading", plugin->path, plugin->handle);
192
            _sir_plugin_destroy(&plugin);
18✔
193
        }
194
    }
195

196
    return retval;
45✔
197
#else
198
    SIR_UNUSED(plugin);
199
    return 0U;
×
200
#endif
201
}
202

203
sir_pluginexport _sir_plugin_getexport(sir_pluginhandle handle, const char* name) {
608✔
204
#if !defined(SIR_NO_PLUGINS)
205
    if (!_sir_validptr(handle) || !_sir_validstr(name))
608✔
206
        return NULL;
×
207

208
# if !defined(__WIN__)
209
    sir_pluginexport addr = NULL;
512✔
210
    *(void**)(&addr) = dlsym(handle, name);
608✔
211

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

239
bool _sir_plugin_unload(sir_plugin* plugin) {
189✔
240
#if !defined(SIR_NO_PLUGINS)
241
    if (!_sir_validptrnofail(plugin) || !_sir_validptrnofail(plugin->handle)) {
189✔
242
        _sir_selflog("error: plugin object (%p) or handle (%p) are null;"
28✔
243
                     " cannot unload!", (void*)plugin, (plugin ? plugin->handle : NULL));
244
        return false;
29✔
245
    }
246

247
    /* if the plugin cleanup export was resolved, call it. */
248
    if (plugin->iface.cleanup && !plugin->iface.cleanup())
160✔
249
        _sir_selflog("warning: plugin (path: '%s', addr: %p) reports unsuccessful"
17✔
250
                     " cleanup!", plugin->path, plugin->handle);
251

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

266
    plugin->handle = NULL;
160✔
267
    plugin->loaded = false;
160✔
268
    _sir_selflog("unloaded plugin (path: '%s', id: %08"PRIx32")", plugin->path,
152✔
269
        plugin->id);
270
    return true;
160✔
271
#else
272
    SIR_UNUSED(plugin);
273
    return false;
×
274
#endif
275
}
276

277
sirpluginid _sir_plugin_add(sir_plugin* plugin) {
54✔
278
#if !defined(SIR_NO_PLUGINS)
279
    if (!_sir_validptr(plugin))
54✔
280
        return 0U;
×
281

282
    _SIR_LOCK_SECTION(sir_plugincache, spc, SIRMI_PLUGINCACHE, 0U);
54✔
283
    sirpluginid retval = _sir_plugin_cache_add(spc, plugin);
54✔
284
    _SIR_UNLOCK_SECTION(SIRMI_PLUGINCACHE);
54✔
285

286
    return retval;
54✔
287
#else
288
    SIR_UNUSED(plugin);
289
    return 0U;
×
290
#endif
291
}
292

293
bool _sir_plugin_rem(sirpluginid id) {
44✔
294
#if !defined(SIR_NO_PLUGINS)
295
    (void)_sir_seterror(_SIR_E_NOERROR);
44✔
296

297
    if (!_sir_sanity())
44✔
298
        return false;
×
299

300
    _SIR_LOCK_SECTION(sir_plugincache, spc, SIRMI_PLUGINCACHE, false);
44✔
301
    bool retval = _sir_plugin_cache_rem(spc, id);
44✔
302
    _SIR_UNLOCK_SECTION(SIRMI_PLUGINCACHE);
44✔
303

304
    return retval;
44✔
305
#else
306
    SIR_UNUSED(id);
307
    return false;
×
308
#endif
309
}
310

311
void _sir_plugin_destroy(sir_plugin** plugin) {
189✔
312
#if !defined(SIR_NO_PLUGINS)
313
    if (_sir_validptrptr(plugin) && _sir_validptr(*plugin)) {
189✔
314
        bool unloaded = _sir_plugin_unload(*plugin);
189✔
315
        SIR_ASSERT_UNUSED(unloaded, unloaded);
180✔
316

317
        _sir_safefree(&(*plugin)->path);
189✔
318
        _sir_safefree(plugin);
189✔
319
    }
320
#else
321
    SIR_UNUSED(plugin);
322
#endif
323
}
189✔
324

325
bool _sir_plugin_cache_pred_id(const void* match, sir_plugin* iter) {
36✔
326
#if !defined(SIR_NO_PLUGINS)
327
    return iter->id == *((sirpluginid*)match);
36✔
328
#else
329
    SIR_UNUSED(match);
330
    SIR_UNUSED(iter);
331
    return false;
×
332
#endif
333
}
334

335
sirpluginid _sir_plugin_cache_add(sir_plugincache* spc, sir_plugin* plugin) {
54✔
336
#if !defined(SIR_NO_PLUGINS)
337
    if (!_sir_validptr(spc) || !_sir_validptr(plugin))
54✔
338
        return 0U;
×
339

340
    if (spc->count >= SIR_MAXPLUGINS) {
54✔
341
        (void)_sir_seterror(_SIR_E_NOROOM);
×
342
        return 0U;
×
343
    }
344

345
    sir_plugin* existing = _sir_plugin_cache_find_id(spc, plugin->id);
54✔
346
    if (NULL != existing) {
54✔
347
        _sir_selflog("error: already have plugin (path: '%s', id %08"PRIx32")",
17✔
348
            existing->path, plugin->id);
349
        (void)_sir_seterror(_SIR_E_DUPITEM);
18✔
350
        return 0U;
18✔
351
    }
352

353
    _sir_selflog("adding plugin (path: %s, id: %08"PRIx32"); count = %zu",
36✔
354
    plugin->path, plugin->id, spc->count + 1);
355
    spc->plugins[spc->count++] = plugin;
36✔
356
    return plugin->id;
36✔
357
#else
358
    SIR_UNUSED(spc);
359
    SIR_UNUSED(plugin);
360
    return 0U;
×
361
#endif
362
}
363

364
sir_plugin* _sir_plugin_cache_find_id(sir_plugincache* spc, sirpluginid id) {
54✔
365
#if !defined(SIR_NO_PLUGINS)
366
    return _sir_plugin_cache_find(spc, &id, &_sir_plugin_cache_pred_id);
54✔
367
#else
368
    SIR_UNUSED(spc);
369
    SIR_UNUSED(id);
370
    return NULL;
×
371
#endif
372
}
373

374
sir_plugin* _sir_plugin_cache_find(sir_plugincache* spc, const void* match,
54✔
375
    sir_plugin_pred pred) {
376
#if !defined(SIR_NO_PLUGINS)
377
    if (!_sir_validptr(spc) || !_sir_validptr(match) || !_sir_validfnptr(pred))
54✔
378
        return NULL;
×
379

380
    for (size_t n = 0; n < spc->count; n++) {
72✔
381
        if (pred(match, spc->plugins[n]))
36✔
382
            return spc->plugins[n];
18✔
383
    }
384

385
    return NULL;
30✔
386
#else
387
    SIR_UNUSED(spc);
388
    SIR_UNUSED(match);
389
    SIR_UNUSED(pred);
390
    return NULL;
×
391
#endif
392
}
393

394
bool _sir_plugin_cache_rem(sir_plugincache* spc, sirpluginid id) {
44✔
395
#if !defined(SIR_NO_PLUGINS)
396
    if (!_sir_validptr(spc))
44✔
397
        return false;
×
398

399
    for (size_t n = 0; n < spc->count; n++) {
62✔
400
        if (spc->plugins[n]->id == id) {
36✔
401
            _sir_selflog("removing plugin (path: '%s', id: %"PRIx32"); count = %zu",
17✔
402
                spc->plugins[n]->path, spc->plugins[n]->id, spc->count - 1);
403

404
            _sir_plugin_destroy(&spc->plugins[n]);
18✔
405

406
            for (size_t i = n; i < spc->count - 1; i++) {
36✔
407
                spc->plugins[i] = spc->plugins[i + 1];
18✔
408
                spc->plugins[i + 1] = NULL;
18✔
409
            }
410

411
            spc->count--;
18✔
412
            return true;
18✔
413
        }
414
    }
415

416
    return _sir_seterror(_SIR_E_NOITEM);
26✔
417
#else
418
    SIR_UNUSED(spc);
419
    SIR_UNUSED(id);
420
    return false;
×
421
#endif
422
}
423

424
bool _sir_plugin_cache_destroy(sir_plugincache* spc) {
704✔
425
#if !defined(SIR_NO_PLUGINS)
426
    if (!_sir_validptr(spc))
704✔
427
        return false;
×
428

429
    while (spc->count > 0) {
722✔
430
        size_t idx = spc->count - 1;
18✔
431
        _sir_plugin_destroy(&spc->plugins[idx]);
18✔
432
        spc->plugins[idx] = NULL;
18✔
433
        spc->count--;
18✔
434
    }
435

436
    memset(spc, 0, sizeof(sir_plugincache));
605✔
437
    return true;
704✔
438
#else
439
    SIR_UNUSED(spc);
440
    return false;
×
441
#endif
442
}
443

444
bool _sir_plugin_cache_dispatch(sir_plugincache* spc, sir_level level, sirbuf* buf,
121,761✔
445
    size_t* dispatched, size_t* wanted) {
446
#if !defined(SIR_NO_PLUGINS)
447
    if (!_sir_validptr(spc) || !_sir_validlevel(level) || !_sir_validptr(buf) ||
141,264✔
448
        !_sir_validptr(dispatched) || !_sir_validptr(wanted))
141,264✔
449
        return false;
×
450

451
    const char* write    = NULL;
102,258✔
452
    sir_options lastopts = 0;
102,258✔
453

454
    *dispatched = 0;
121,761✔
455
    *wanted     = 0;
121,761✔
456

457
    for (size_t n = 0; n < spc->count; n++) {
121,833✔
458
        if (!_sir_bittest(spc->plugins[n]->info.levels, level)) {
72✔
459
            _sir_selflog("level %04"PRIx16" not set in level mask (%04"PRIx16
17✔
460
                         ") for plugin (path: '%s', id: %08"PRIx32"); skipping",
461
                         level, spc->plugins[n]->info.levels, spc->plugins[n]->path,
462
                         spc->plugins[n]->id);
463
            continue;
18✔
464
        }
465

466
        (*wanted)++;
54✔
467

468
        if (!write || spc->plugins[n]->info.opts != lastopts) {
54✔
469
            write = _sir_format(false, spc->plugins[n]->info.opts, buf);
36✔
470
            SIR_ASSERT(write);
34✔
471
            lastopts = spc->plugins[n]->info.opts;
36✔
472
        }
473

474
        if (write && spc->plugins[n]->iface.write(level, write)) {
54✔
475
            (*dispatched)++;
36✔
476
        } else {
477
            _sir_selflog("error: write to plugin (path: '%s', id: %08"PRIx32")"
17✔
478
                         " failed!", spc->plugins[n]->path, spc->plugins[n]->id);
479
        }
480
    }
481

482
    return (*dispatched == *wanted);
121,761✔
483
#else
484
    SIR_UNUSED(spc);
485
    SIR_UNUSED(level);
486
    SIR_UNUSED(buf);
487
    SIR_UNUSED(dispatched);
488
    SIR_UNUSED(wanted);
489
    return false;
×
490
#endif
491
}
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