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

aremmell / libsir / 1153

25 Jul 2023 04:30AM UTC coverage: 80.118% (-14.7%) from 94.801%
1153

push

travis-ci

aremmell
fix ternary

1 of 1 new or added line in 1 file covered. (100.0%)

2571 of 3209 relevant lines covered (80.12%)

24530.87 hits per line

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

82.23
/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);
×
40

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

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

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

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

79
sirpluginid _sir_plugin_probe(sir_plugin* plugin) {
24✔
80
#if !defined(SIR_NO_PLUGINS)
81
    sirpluginid retval = 0;
×
82
    if (plugin) {
24✔
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)
24✔
94
            _sir_plugin_getexport(plugin->handle, SIR_PLUGIN_EXPORT_QUERY);
24✔
95
        plugin->iface.init    = (sir_plugin_initfn)
24✔
96
            _sir_plugin_getexport(plugin->handle, SIR_PLUGIN_EXPORT_INIT);
24✔
97
        plugin->iface.write   = (sir_plugin_writefn)
24✔
98
            _sir_plugin_getexport(plugin->handle, SIR_PLUGIN_EXPORT_WRITE);
24✔
99
        plugin->iface.cleanup = (sir_plugin_cleanupfn)
24✔
100
            _sir_plugin_getexport(plugin->handle, SIR_PLUGIN_EXPORT_CLEANUP);
24✔
101

102
        if (!plugin->iface.query || !plugin->iface.init ||
24✔
103
            !plugin->iface.write || !plugin->iface.cleanup) {
21✔
104
            _sir_selflog("error: export(s) not resolved for plugin (path:"
2✔
105
                        " '%s', addr: %p)!", plugin->path, plugin->handle);
106
            _sir_selflog("exports (query: %"PRIxPTR", init: %"PRIxPTR", write:"
2✔
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);
3✔
111
            return _sir_seterror(_SIR_E_PLUGINBAD);
3✔
112
        }
113
# else
114
#  error "plugin version not implemented"
115
# endif
116
        /* query the plugin for information. */
117
        if (!plugin->iface.query(&plugin->info)) {
21✔
118
            _sir_selflog("error: plugin (path: '%s', addr: %p) returned false from"
2✔
119
                        " query fn!", plugin->path, plugin->handle);
120
            _sir_plugin_destroy(&plugin);
3✔
121
            return _sir_seterror(_SIR_E_PLUGINERR);
3✔
122
        }
123

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

133
        bool data_valid = true;
×
134

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

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

149
        /* verify strings */
150
        if (!_sir_validstrnofail(plugin->info.author) ||
27✔
151
            !_sir_validstrnofail(plugin->info.desc)) {
12✔
152
            _sir_selflog("error: plugin (path: '%s', addr: %p) has invalid author"
2✔
153
                        " or description", plugin->path, plugin->handle);
154
            data_valid = false;
×
155
        }
156

157
        /* if any category of data is invalid, fail and unload. */
158
        if (!data_valid) {
12✔
159
            _sir_plugin_destroy(&plugin);
3✔
160
            return _sir_seterror(_SIR_E_PLUGINDAT);
3✔
161
        }
162

163
        /* plugin is valid; tell it to initialize, assign it an id,
164
        * print its information, and add to cache. */
165
        if (!plugin->iface.init()) {
12✔
166
            _sir_selflog("error: plugin (path: '%s', addr: %p) failed to initialize!",
2✔
167
                plugin->path, plugin->handle);
168
            _sir_plugin_destroy(&plugin);
3✔
169
            return _sir_seterror(_SIR_E_PLUGINERR);
3✔
170
        }
171

172
        plugin->id    = FNV32_1a((const uint8_t*)&plugin->iface, sizeof(sir_pluginiface));
9✔
173
        plugin->valid = true;
9✔
174

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

184
        retval = _sir_plugin_add(plugin);
9✔
185
        if (0 == retval) {
9✔
186
            _sir_selflog("error: failed to add plugin (path: '%s', addr: %p) to"
2✔
187
                        " cache; unloading", plugin->path, plugin->handle);
188
            _sir_plugin_destroy(&plugin);
3✔
189
        }
190
    }
191

192
    return retval;
×
193
#else
194
    SIR_UNUSED(plugin);
195
    return 0;
×
196
#endif
197
}
198

199
uintptr_t _sir_plugin_getexport(sir_pluginhandle handle, const char* name) {
96✔
200
#if !defined(SIR_NO_PLUGINS)
201
    if (!_sir_validptr(handle) || !_sir_validstr(name))
96✔
202
        return 0;
×
203

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

222
    _sir_selflog("successfully resolved plugin export (name: '%s', addr: %p)",
62✔
223
        name, addr);
224
    return (uintptr_t)addr;
93✔
225
#else
226
    SIR_UNUSED(handle);
227
    SIR_UNUSED(name);
228
    return 0;
×
229
#endif
230
}
231

232
bool _sir_plugin_unload(sir_plugin* plugin) {
27✔
233
#if !defined(SIR_NO_PLUGINS)
234
    if (!_sir_validptrnofail(plugin) || !_sir_validptrnofail(plugin->handle)) {
27✔
235
        _sir_selflog("error: plugin object (%p) or handle (%p) are null;"
2✔
236
                     " cannot unload!", (void*)plugin, (plugin ? plugin->handle : NULL));
237
        return false;
3✔
238
    }
239

240
    /* if the plugin cleanup export was resolved, call it. */
241
    if (plugin->iface.cleanup && !plugin->iface.cleanup())
24✔
242
        _sir_selflog("warning: plugin (path: '%s', addr: %p) reports unsuccessful"
2✔
243
                     " cleanup!", plugin->path, plugin->handle);
244

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

259
    plugin->handle = NULL;
24✔
260
    plugin->loaded = false;
24✔
261
    _sir_selflog("unloaded plugin (path: '%s', id: %08"PRIx32")", plugin->path,
16✔
262
        plugin->id);
263
    return true;
24✔
264
#else
265
    SIR_UNUSED(plugin);
266
    return false;
×
267
#endif
268
}
269

270
sirpluginid _sir_plugin_add(sir_plugin* plugin) {
9✔
271
#if !defined(SIR_NO_PLUGINS)
272
    if (!_sir_validptr(plugin))
9✔
273
        return 0;
×
274

275
    _SIR_LOCK_SECTION(sir_plugincache, spc, SIRMI_PLUGINCACHE, 0);
9✔
276
    sirpluginid retval = _sir_plugin_cache_add(spc, plugin);
9✔
277
    _SIR_UNLOCK_SECTION(SIRMI_PLUGINCACHE);
9✔
278

279
    return retval;
9✔
280
#else
281
    SIR_UNUSED(plugin);
282
    return 0;
×
283
#endif
284
}
285

286
bool _sir_plugin_rem(sirpluginid id) {
6✔
287
#if !defined(SIR_NO_PLUGINS)
288
    (void)_sir_seterror(_SIR_E_NOERROR);
6✔
289

290
    if (!_sir_sanity())
6✔
291
        return false;
×
292

293
    _SIR_LOCK_SECTION(sir_plugincache, spc, SIRMI_PLUGINCACHE, false);
6✔
294
    bool retval = _sir_plugin_cache_rem(spc, id);
6✔
295
    _SIR_UNLOCK_SECTION(SIRMI_PLUGINCACHE);
6✔
296

297
    return retval;
6✔
298
#else
299
    SIR_UNUSED(id);
300
    return false;
×
301
#endif
302
}
303

304
void _sir_plugin_destroy(sir_plugin** plugin) {
27✔
305
#if !defined(SIR_NO_PLUGINS)
306
    if (_sir_validptrptr(plugin) && _sir_validptr(*plugin)) {
27✔
307
        bool unloaded = _sir_plugin_unload(*plugin);
27✔
308
        SIR_ASSERT_UNUSED(unloaded, unloaded);
18✔
309

310
        _sir_safefree(&(*plugin)->path);
27✔
311
        _sir_safefree(plugin);
27✔
312
    }
313
#else
314
    SIR_UNUSED(plugin);
315
#endif
316
}
27✔
317

318
bool _sir_plugin_cache_pred_id(const void* match, sir_plugin* iter) {
6✔
319
#if !defined(SIR_NO_PLUGINS)
320
    return iter->id == *((sirpluginid*)match);
6✔
321
#else
322
    SIR_UNUSED(match);
323
    SIR_UNUSED(iter);
324
    return false;
×
325
#endif
326
}
327

328
sirpluginid _sir_plugin_cache_add(sir_plugincache* spc, sir_plugin* plugin) {
9✔
329
#if !defined(SIR_NO_PLUGINS)
330
    if (!_sir_validptr(spc) || !_sir_validptr(plugin))
9✔
331
        return 0;
×
332

333
    if (spc->count >= SIR_MAXPLUGINS)
9✔
334
        return _sir_seterror(_SIR_E_NOROOM);
×
335

336
    sir_plugin* existing = _sir_plugin_cache_find_id(spc, plugin->id);
9✔
337
    if (NULL != existing) {
9✔
338
        _sir_selflog("error: already have plugin (path: '%s', id %08"PRIx32")",
2✔
339
            existing->path, plugin->id);
340
        return _sir_seterror(_SIR_E_DUPITEM);
3✔
341
    }
342

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

354
sir_plugin* _sir_plugin_cache_find_id(sir_plugincache* spc, sirpluginid id) {
9✔
355
#if !defined(SIR_NO_PLUGINS)
356
    return _sir_plugin_cache_find(spc, &id, &_sir_plugin_cache_pred_id);
9✔
357
#else
358
    SIR_UNUSED(spc);
359
    SIR_UNUSED(id);
360
    return NULL;
×
361
#endif
362
}
363

364
sir_plugin* _sir_plugin_cache_find(sir_plugincache* spc, const void* match,
9✔
365
    sir_plugin_pred pred) {
366
#if !defined(SIR_NO_PLUGINS)
367
    if (!_sir_validptr(spc) || !_sir_validptr(match) || !_sir_validfnptr(pred))
9✔
368
        return NULL;
×
369

370
    for (size_t n = 0; n < spc->count; n++) {
12✔
371
        if (pred(match, spc->plugins[n]))
6✔
372
            return spc->plugins[n];
3✔
373
    }
374

375
    return NULL;
×
376
#else
377
    SIR_UNUSED(spc);
378
    SIR_UNUSED(match);
379
    SIR_UNUSED(pred);
380
    return NULL;
×
381
#endif
382
}
383

384
bool _sir_plugin_cache_rem(sir_plugincache* spc, sirpluginid id) {
6✔
385
#if !defined(SIR_NO_PLUGINS)
386
    if (!_sir_validptr(spc))
6✔
387
        return false;
×
388

389
    for (size_t n = 0; n < spc->count; n++) {
9✔
390
        if (spc->plugins[n]->id == id) {
6✔
391
            _sir_selflog("removing plugin (path: '%s', id: %"PRIx32"); count = %zu",
2✔
392
                spc->plugins[n]->path, spc->plugins[n]->id, spc->count - 1);
393

394
            _sir_plugin_destroy(&spc->plugins[n]);
3✔
395

396
            for (size_t i = n; i < spc->count - 1; i++) {
6✔
397
                spc->plugins[i] = spc->plugins[i + 1];
3✔
398
                spc->plugins[i + 1] = NULL;
3✔
399
            }
400

401
            spc->count--;
3✔
402
            return true;
3✔
403
        }
404
    }
405

406
    return _sir_seterror(_SIR_E_NOITEM);
3✔
407
#else
408
    SIR_UNUSED(spc);
409
    SIR_UNUSED(id);
410
    return false;
×
411
#endif
412
}
413

414
bool _sir_plugin_cache_destroy(sir_plugincache* spc) {
118✔
415
#if !defined(SIR_NO_PLUGINS)
416
    if (!_sir_validptr(spc))
118✔
417
        return false;
×
418

419
    while (spc->count > 0) {
121✔
420
        size_t idx = spc->count - 1;
3✔
421
        _sir_plugin_destroy(&spc->plugins[idx]);
3✔
422
        spc->plugins[idx] = NULL;
3✔
423
        spc->count--;
3✔
424
    }
425

426
    memset(spc, 0, sizeof(sir_plugincache));
19✔
427
    return true;
118✔
428
#else
429
    SIR_UNUSED(spc);
430
    return false;
×
431
#endif
432
}
433

434
bool _sir_plugin_cache_dispatch(sir_plugincache* spc, sir_level level, sirbuf* buf,
19,750✔
435
    size_t* dispatched, size_t* wanted) {
436
#if !defined(SIR_NO_PLUGINS)
437
    if (!_sir_validptr(spc) || !_sir_validlevel(level) || !_sir_validptr(buf) ||
39,253✔
438
        !_sir_validptr(dispatched) || !_sir_validptr(wanted))
39,253✔
439
        return false;
×
440

441
    const char* write    = NULL;
247✔
442
    sir_options lastopts = 0;
247✔
443

444
    *dispatched = 0;
19,750✔
445
    *wanted     = 0;
19,750✔
446

447
    for (size_t n = 0; n < spc->count; n++) {
19,762✔
448
        if (!_sir_bittest(spc->plugins[n]->info.levels, level)) {
12✔
449
            _sir_selflog("level %04"PRIx32" not set in level mask (%04"PRIx16
2✔
450
                         ") for plugin (path: '%s', id: %08"PRIx32"); skipping",
451
                         level, spc->plugins[n]->info.levels, spc->plugins[n]->path,
452
                         spc->plugins[n]->id);
453
            continue;
3✔
454
        }
455

456
        (*wanted)++;
9✔
457

458
        if (!write || spc->plugins[n]->info.opts != lastopts) {
9✔
459
            write = _sir_format(false, spc->plugins[n]->info.opts, buf);
6✔
460
            SIR_ASSERT(write);
4✔
461
            lastopts = spc->plugins[n]->info.opts;
6✔
462
        }
463

464
        if (write && spc->plugins[n]->iface.write(level, write)) {
9✔
465
            (*dispatched)++;
6✔
466
        } else {
467
            _sir_selflog("error: write to plugin (path: '%s', id: %08"PRIx32")"
2✔
468
                         " failed!", spc->plugins[n]->path, spc->plugins[n]->id);
469
        }
470
    }
471

472
    return (*dispatched == *wanted);
19,750✔
473
#else
474
    SIR_UNUSED(spc);
475
    SIR_UNUSED(level);
476
    SIR_UNUSED(buf);
477
    SIR_UNUSED(dispatched);
478
    SIR_UNUSED(wanted);
479
    return false;
×
480
#endif
481
}
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