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

aremmell / libsir / 1125

24 Jul 2023 02:39PM UTC coverage: 87.776% (-7.0%) from 94.766%
1125

Pull #193

travis-ci

web-flow
Merge d7efcf9ae into 13f9deedb
Pull Request #193: Add linting script

7 of 7 new or added lines in 2 files covered. (100.0%)

2822 of 3215 relevant lines covered (87.78%)

1500251.95 hits per line

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

30.0
/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.1
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) {
30✔
31
#if !defined(SIR_NO_PLUGINS)
32
    (void)_sir_seterror(_SIR_E_NOERROR);
30✔
33

34
    if (!_sir_sanity() || !_sir_validstr(path))
30✔
35
        return 0;
3✔
36

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

41
# if !defined (__WIN__)
42
    plugin->handle = dlopen(path, RTLD_NOW | RTLD_LOCAL);
18✔
43
    if (!plugin->handle) {
18✔
44
        const char* err = dlerror();
10✔
45
        _sir_selflog("error: dlopen('%s') failed (%s)", path, _SIR_PRNSTR(err));
10✔
46
        _sir_plugin_destroy(&plugin);
10✔
47
        return _sir_handleerr(errno);
10✔
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;
8✔
62
    plugin->path   = strndup(path, strnlen(path, SIR_MAXPATH));
8✔
63

64
    if (!_sir_validstrnofail(plugin->path)) {
8✔
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
×
70
        plugin->path, plugin->handle);
71

72
    sirpluginid retval =  _sir_plugin_probe(plugin);
×
73
    if (0 == retval)
×
74
        _sir_plugin_destroy(&plugin);
×
75

76
    return retval;
×
77
#else
78
    SIR_UNUSED(path);
79
    return 0;
×
80
#endif
81
}
82

83
sirpluginid _sir_plugin_probe(sir_plugin* plugin) {
×
84
#if !defined(SIR_NO_PLUGINS)
85
    if (!_sir_validptr(plugin))
×
86
        return 0;
×
87

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)
×
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
    /* query the plugin for information. */
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
        return _sir_seterror(_SIR_E_PLUGINERR);
×
127
    }
128

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

138
    bool data_valid = true;
×
139

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

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

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

162
    /* if any category of data is invalid, fail and unload. */
163
    if (!data_valid) {
×
164
        _sir_plugin_destroy(&plugin);
×
165
        return _sir_seterror(_SIR_E_PLUGINDAT);
×
166
    }
167

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

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

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

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

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

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

208
# if !defined(__WIN__)
209
    sir_pluginexport addr = dlsym(handle, name);
×
210
    if (!addr) {
×
211
        const char* err = dlerror();
×
212
        _sir_selflog("error: dlsym(%p, '%s') failed (%s)", handle, name,
×
213
            _SIR_PRNSTR(err));
214
        return _sir_handleerr(errno);
×
215
    }
216
# else /* __WIN__ */
217
    sir_pluginexport addr = GetProcAddress(handle, name);
218
    if (!addr) {
219
        DWORD err = GetLastError();
220
        _sir_selflog("error: GetProcAddress(%p, '%s') failed (%lu)", handle,
221
            name, err);
222
        return _sir_handlewin32err(err);
223
    }
224
# endif
225

226
    _sir_selflog("successfully resolved plugin export (name: '%s', addr: %p)",
×
227
        name, addr);
228
    return (uintptr_t)addr;
×
229
#else
230
    SIR_UNUSED(handle);
231
    SIR_UNUSED(name);
232
    return 0;
×
233
#endif
234
}
235

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

244
    /* if the plugin cleanup export was resolved, call it. */
245
    if (plugin->iface.cleanup && !plugin->iface.cleanup())
8✔
246
        _sir_selflog("warning: plugin (path: '%s', addr: %p) reports unsuccessful"
×
247
                     " cleanup!", plugin->path, plugin->handle);
248

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

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

274
sirpluginid _sir_plugin_add(sir_plugin* plugin) {
×
275
#if !defined(SIR_NO_PLUGINS)
276
    if (!_sir_validptr(plugin))
×
277
        return 0;
×
278

279
    _SIR_LOCK_SECTION(sir_plugincache, spc, SIRMI_PLUGINCACHE, 0);
×
280
    sirpluginid retval = _sir_plugin_cache_add(spc, plugin);
×
281
    _SIR_UNLOCK_SECTION(SIRMI_PLUGINCACHE);
×
282

283
    return retval;
×
284
#else
285
    SIR_UNUSED(plugin);
286
    return 0;
×
287
#endif
288
}
289

290
bool _sir_plugin_rem(sirpluginid id) {
6✔
291
#if !defined(SIR_NO_PLUGINS)
292
    (void)_sir_seterror(_SIR_E_NOERROR);
6✔
293

294
    if (!_sir_sanity())
6✔
295
        return false;
×
296

297
    _SIR_LOCK_SECTION(sir_plugincache, spc, SIRMI_PLUGINCACHE, false);
6✔
298
    bool retval = _sir_plugin_cache_rem(spc, id);
6✔
299
    _SIR_UNLOCK_SECTION(SIRMI_PLUGINCACHE);
6✔
300

301
    return retval;
6✔
302
#else
303
    SIR_UNUSED(id);
304
    return false;
×
305
#endif
306
}
307

308
void _sir_plugin_destroy(sir_plugin** plugin) {
18✔
309
#if !defined(SIR_NO_PLUGINS)
310
    if (_sir_validptrptr(plugin) && _sir_validptr(*plugin)) {
18✔
311
        bool unloaded = _sir_plugin_unload(*plugin);
18✔
312
        SIR_ASSERT_UNUSED(unloaded, unloaded);
18✔
313

314
        _sir_safefree(&(*plugin)->path);
18✔
315
        _sir_safefree(plugin);
18✔
316
    }
317
#else
318
    SIR_UNUSED(plugin);
319
#endif
320
}
18✔
321

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

332
sirpluginid _sir_plugin_cache_add(sir_plugincache* spc, sir_plugin* plugin) {
×
333
#if !defined(SIR_NO_PLUGINS)
334
    if (!_sir_validptr(spc) || !_sir_validptr(plugin))
×
335
        return 0;
×
336

337
    if (spc->count >= SIR_MAXPLUGINS)
×
338
        return _sir_seterror(_SIR_E_NOROOM);
×
339

340
    sir_plugin* existing = _sir_plugin_cache_find_id(spc, plugin->id);
×
341
    if (NULL != existing) {
×
342
        _sir_selflog("error: already have plugin (path: '%s', id %08"PRIx32")",
×
343
            existing->path, plugin->id);
344
        return _sir_seterror(_SIR_E_DUPITEM);
×
345
    }
346

347
    _sir_selflog("adding plugin (path: %s, id: %08"PRIx32"); count = %zu",
×
348
    plugin->path, plugin->id, spc->count + 1);
349
    spc->plugins[spc->count++] = plugin;
×
350
    return plugin->id;
×
351
#else
352
    SIR_UNUSED(spc);
353
    SIR_UNUSED(plugin);
354
    return 0;
×
355
#endif
356
}
357

358
sir_plugin* _sir_plugin_cache_find_id(sir_plugincache* spc, sirpluginid id) {
×
359
#if !defined(SIR_NO_PLUGINS)
360
    return _sir_plugin_cache_find(spc, &id, &_sir_plugin_cache_pred_id);
×
361
#else
362
    SIR_UNUSED(spc);
363
    SIR_UNUSED(id);
364
    return NULL;
×
365
#endif
366
}
367

368
sir_plugin* _sir_plugin_cache_find(sir_plugincache* spc, const void* match,
×
369
    sir_plugin_pred pred) {
370
#if !defined(SIR_NO_PLUGINS)
371
    if (!_sir_validptr(spc) || !_sir_validptr(match) || !_sir_validfnptr(pred))
×
372
        return NULL;
×
373

374
    for (size_t n = 0; n < spc->count; n++) {
×
375
        if (pred(match, spc->plugins[n]))
×
376
            return spc->plugins[n];
×
377
    }
378

379
    return NULL;
×
380
#else
381
    SIR_UNUSED(spc);
382
    SIR_UNUSED(match);
383
    SIR_UNUSED(pred);
384
    return NULL;
×
385
#endif
386
}
387

388
bool _sir_plugin_cache_rem(sir_plugincache* spc, sirpluginid id) {
6✔
389
#if !defined(SIR_NO_PLUGINS)
390
    if (!_sir_validptr(spc))
6✔
391
        return false;
×
392

393
    for (size_t n = 0; n < spc->count; n++) {
6✔
394
        if (spc->plugins[n]->id == id) {
×
395
            _sir_selflog("removing plugin (path: '%s', id: %"PRIx32"); count = %zu",
×
396
                spc->plugins[n]->path, spc->plugins[n]->id, spc->count - 1);
397

398
            _sir_plugin_destroy(&spc->plugins[n]);
×
399

400
            for (size_t i = n; i < spc->count - 1; i++) {
×
401
                spc->plugins[i] = spc->plugins[i + 1];
×
402
                spc->plugins[i + 1] = NULL;
×
403
            }
404

405
            spc->count--;
×
406
            return true;
×
407
        }
408
    }
409

410
    return _sir_seterror(_SIR_E_NOITEM);
6✔
411
#else
412
    SIR_UNUSED(spc);
413
    SIR_UNUSED(id);
414
    return false;
×
415
#endif
416
}
417

418
bool _sir_plugin_cache_destroy(sir_plugincache* spc) {
122✔
419
#if !defined(SIR_NO_PLUGINS)
420
    if (!_sir_validptr(spc))
122✔
421
        return false;
×
422

423
    while (spc->count > 0) {
122✔
424
        size_t idx = spc->count - 1;
×
425
        _sir_plugin_destroy(&spc->plugins[idx]);
×
426
        spc->plugins[idx] = NULL;
×
427
        spc->count--;
×
428
    }
429

430
    memset(spc, 0, sizeof(sir_plugincache));
119✔
431
    return true;
122✔
432
#else
433
    SIR_UNUSED(spc);
434
    return false;
×
435
#endif
436
}
437

438
bool _sir_plugin_cache_dispatch(sir_plugincache* spc, sir_level level, sirbuf* buf,
11,322✔
439
    size_t* dispatched, size_t* wanted) {
440
#if !defined(SIR_NO_PLUGINS)
441
    if (!_sir_validptr(spc) || !_sir_validlevel(level) || !_sir_validptr(buf) ||
11,361✔
442
        !_sir_validptr(dispatched) || !_sir_validptr(wanted))
11,361✔
443
        return false;
×
444

445
    const char* write    = NULL;
11,283✔
446
    sir_options lastopts = 0;
11,283✔
447

448
    *dispatched = 0;
11,322✔
449
    *wanted     = 0;
11,322✔
450

451
    for (size_t n = 0; n < spc->count; n++) {
11,322✔
452
        if (!_sir_bittest(spc->plugins[n]->info.levels, level)) {
×
453
            _sir_selflog("level %04"PRIx32" not set in level mask (%04"PRIx16
×
454
                         ") for plugin (path: '%s', id: %08"PRIx32"); skipping",
455
                         level, spc->plugins[n]->info.levels, spc->plugins[n]->path,
456
                         spc->plugins[n]->id);
457
            continue;
×
458
        }
459

460
        (*wanted)++;
×
461

462
        if (!write || spc->plugins[n]->info.opts != lastopts) {
×
463
            write = _sir_format(false, spc->plugins[n]->info.opts, buf);
×
464
            SIR_ASSERT(write);
×
465
            lastopts = spc->plugins[n]->info.opts;
×
466
        }
467

468
        if (write && spc->plugins[n]->iface.write(level, write)) {
×
469
            (*dispatched)++;
×
470
        } else {
471
            _sir_selflog("error: write to plugin (path: '%s', id: %08"PRIx32")"
×
472
                         " failed!", spc->plugins[n]->path, spc->plugins[n]->id);
473
        }
474
    }
475

476
    return (*dispatched == *wanted);
11,322✔
477
#else
478
    SIR_UNUSED(spc);
479
    SIR_UNUSED(level);
480
    SIR_UNUSED(buf);
481
    SIR_UNUSED(dispatched);
482
    SIR_UNUSED(wanted);
483
    return false;
×
484
#endif
485
}
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