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

aremmell / libsir / 1128

24 Jul 2023 03:06PM UTC coverage: 94.818% (+7.0%) from 87.837%
1128

push

travis-ci

aremmell
free job data on destruction of non-empty queue

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

3056 of 3223 relevant lines covered (94.82%)

1628895.1 hits per line

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

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

34
    if (!_sir_sanity() || !_sir_validstr(path))
200✔
35
        return 0;
20✔
36

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

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

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

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

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

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

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

133
        bool data_valid = true;
65✔
134

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

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

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

157
        /* if any category of data is invalid, fail and unload. */
158
        if (!data_valid) {
77✔
159
            _sir_plugin_destroy(&plugin);
16✔
160
            return _sir_seterror(_SIR_E_PLUGINDAT);
16✔
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()) {
64✔
166
            _sir_selflog("error: plugin (path: '%s', addr: %p) failed to initialize!",
15✔
167
                plugin->path, plugin->handle);
168
            _sir_plugin_destroy(&plugin);
16✔
169
            return _sir_seterror(_SIR_E_PLUGINERR);
16✔
170
        }
171

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

175
        _sir_selflog("successfully validated plugin (path: '%s', id: %08"PRIx32");"
57✔
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);
48✔
185
        if (0 == retval) {
48✔
186
            _sir_selflog("error: failed to add plugin (path: '%s', addr: %p) to"
15✔
187
                        " cache; unloading", plugin->path, plugin->handle);
188
            _sir_plugin_destroy(&plugin);
16✔
189
        }
190
    }
191

192
    return retval;
39✔
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) {
544✔
200
#if !defined(SIR_NO_PLUGINS)
201
    if (!_sir_validptr(handle) || !_sir_validstr(name))
544✔
202
        return 0;
×
203

204
# if !defined(__WIN__)
205
    sir_pluginexport addr = dlsym(handle, name);
544✔
206
    if (!addr) {
544✔
207
        const char* err = dlerror();
48✔
208
        _sir_selflog("error: dlsym(%p, '%s') failed (%s)", handle, name,
47✔
209
            _SIR_PRNSTR(err));
210
        return _sir_handleerr(errno);
48✔
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)",
465✔
223
        name, addr);
224
    return (uintptr_t)addr;
496✔
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) {
171✔
233
#if !defined(SIR_NO_PLUGINS)
234
    if (!_sir_validptrnofail(plugin) || !_sir_validptrnofail(plugin->handle)) {
171✔
235
        _sir_selflog("error: plugin object (%p) or handle (%p) are null;"
26✔
236
                     " cannot unload!", (void*)plugin, (plugin ? plugin->handle : NULL));
237
        return false;
27✔
238
    }
239

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

245
# if !defined(__WIN__)
246
    if (0 != dlclose(plugin->handle)) {
144✔
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;
144✔
260
    plugin->loaded = false;
144✔
261
    _sir_selflog("unloaded plugin (path: '%s', id: %08"PRIx32")", plugin->path,
136✔
262
        plugin->id);
263
    return true;
144✔
264
#else
265
    SIR_UNUSED(plugin);
266
    return false;
×
267
#endif
268
}
269

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

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

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

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

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

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

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

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

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

318
bool _sir_plugin_cache_pred_id(const void* match, sir_plugin* iter) {
32✔
319
#if !defined(SIR_NO_PLUGINS)
320
    return iter->id == *((sirpluginid*)match);
32✔
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) {
48✔
329
#if !defined(SIR_NO_PLUGINS)
330
    if (!_sir_validptr(spc) || !_sir_validptr(plugin))
48✔
331
        return 0;
×
332

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

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

343
    _sir_selflog("adding plugin (path: %s, id: %08"PRIx32"); count = %zu",
32✔
344
    plugin->path, plugin->id, spc->count + 1);
345
    spc->plugins[spc->count++] = plugin;
32✔
346
    return plugin->id;
32✔
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) {
48✔
355
#if !defined(SIR_NO_PLUGINS)
356
    return _sir_plugin_cache_find(spc, &id, &_sir_plugin_cache_pred_id);
48✔
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,
48✔
365
    sir_plugin_pred pred) {
366
#if !defined(SIR_NO_PLUGINS)
367
    if (!_sir_validptr(spc) || !_sir_validptr(match) || !_sir_validfnptr(pred))
48✔
368
        return NULL;
×
369

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

375
    return NULL;
26✔
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) {
40✔
385
#if !defined(SIR_NO_PLUGINS)
386
    if (!_sir_validptr(spc))
40✔
387
        return false;
×
388

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

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

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

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

406
    return _sir_seterror(_SIR_E_NOITEM);
24✔
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) {
639✔
415
#if !defined(SIR_NO_PLUGINS)
416
    if (!_sir_validptr(spc))
639✔
417
        return false;
×
418

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

426
    memset(spc, 0, sizeof(sir_plugincache));
540✔
427
    return true;
639✔
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,
112,789✔
435
    size_t* dispatched, size_t* wanted) {
436
#if !defined(SIR_NO_PLUGINS)
437
    if (!_sir_validptr(spc) || !_sir_validlevel(level) || !_sir_validptr(buf) ||
132,293✔
438
        !_sir_validptr(dispatched) || !_sir_validptr(wanted))
132,293✔
439
        return false;
×
440

441
    const char* write    = NULL;
93,285✔
442
    sir_options lastopts = 0;
93,285✔
443

444
    *dispatched = 0;
112,789✔
445
    *wanted     = 0;
112,789✔
446

447
    for (size_t n = 0; n < spc->count; n++) {
112,853✔
448
        if (!_sir_bittest(spc->plugins[n]->info.levels, level)) {
64✔
449
            _sir_selflog("level %04"PRIx32" not set in level mask (%04"PRIx16
15✔
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;
16✔
454
        }
455

456
        (*wanted)++;
48✔
457

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

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

472
    return (*dispatched == *wanted);
112,789✔
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

© 2026 Coveralls, Inc