Coverage Report

Created: 2026-11-12 07:29

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/fluent-bit/src/flb_plugin.c
Line
Count
Source
1
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
3
/*  Fluent Bit
4
 *  ==========
5
 *  Copyright (C) 2015-2026 The Fluent Bit Authors
6
 *
7
 *  Licensed under the Apache License, Version 2.0 (the "License");
8
 *  you may not use this file except in compliance with the License.
9
 *  You may obtain a copy of the License at
10
 *
11
 *      http://www.apache.org/licenses/LICENSE-2.0
12
 *
13
 *  Unless required by applicable law or agreed to in writing, software
14
 *  distributed under the License is distributed on an "AS IS" BASIS,
15
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
 *  See the License for the specific language governing permissions and
17
 *  limitations under the License.
18
 */
19
20
#include <fluent-bit/flb_compat.h>
21
#include <fluent-bit/flb_info.h>
22
#include <stddef.h>
23
#include <fluent-bit/flb_mem.h>
24
#include <fluent-bit/flb_log.h>
25
#include <fluent-bit/flb_error.h>
26
#include <fluent-bit/flb_kv.h>
27
#include <fluent-bit/flb_config_format.h>
28
#include <fluent-bit/flb_utils.h>
29
#include <fluent-bit/flb_plugin.h>
30
#include <fluent-bit/flb_plugin_proxy.h>
31
32
#include <cfl/cfl_sds.h>
33
#include <cfl/cfl_variant.h>
34
#include <cfl/cfl_kvlist.h>
35
36
#include <sys/types.h>
37
#include <sys/stat.h>
38
39
0
#define PLUGIN_PREFIX           "flb-"
40
0
#define PLUGIN_EXTENSION        ".so"
41
0
#define PLUGIN_STRUCT_SUFFIX    "_plugin"
42
#define PLUGIN_STR_MIN                                              \
43
0
    ((sizeof(PLUGIN_PREFIX) - 1) + sizeof(PLUGIN_EXTENSION) - 1)
44
45
static int is_input(char *name)
46
0
{
47
0
    if (strncmp(name, "in_", 3) == 0) {
48
0
        return FLB_TRUE;
49
0
    }
50
51
0
    return FLB_FALSE;
52
0
}
53
54
static int is_filter(char *name)
55
0
{
56
0
    if (strncmp(name, "filter_", 7) == 0) {
57
0
        return FLB_TRUE;
58
0
    }
59
60
0
    return FLB_FALSE;
61
0
}
62
63
static int is_processor(char *name)
64
0
{
65
0
    if (strncmp(name, "processor_", 10) == 0) {
66
0
        return FLB_TRUE;
67
0
    }
68
69
0
    return FLB_FALSE;
70
0
}
71
72
static int is_output(char *name)
73
0
{
74
0
    if (strncmp(name, "out_", 4) == 0) {
75
0
        return FLB_TRUE;
76
0
    }
77
78
0
    return FLB_FALSE;
79
0
}
80
81
static void *get_handle(const char *path)
82
0
{
83
0
    void *handle;
84
85
0
    handle = dlopen(path, RTLD_LAZY);
86
0
    if (!handle) {
87
0
        flb_error("[plugin] dlopen() %s", dlerror());
88
0
        return NULL;
89
0
    }
90
91
0
    return handle;
92
0
}
93
94
static void *load_symbol(void *dso_handle, const char *symbol)
95
0
{
96
0
    void *s;
97
98
0
    dlerror();
99
0
    s = dlsym(dso_handle, symbol);
100
0
    if (dlerror() != NULL) {
101
0
        return NULL;
102
0
    }
103
0
    return s;
104
0
}
105
106
/*
107
 * From a given path file (.so file), retrieve the expected structure name
108
 * used to perform the plugin registration.
109
 */
110
static char *path_to_plugin_name(char *path)
111
0
{
112
0
    int len;
113
0
    int o_len;
114
0
    char *bname;
115
0
    char *name;
116
0
    char *p;
117
118
    /* Get the basename of the file */
119
0
    bname = basename(path);
120
0
    if (!bname) {
121
0
        flb_error("[plugin] could not resolve basename(3) of the plugin");
122
0
        return NULL;
123
0
    }
124
0
    len = strlen(bname);
125
126
0
    if (len < PLUGIN_STR_MIN) {
127
0
        flb_error("[plugin] invalid plugin name: %s", bname);
128
0
        return NULL;
129
0
    }
130
131
0
    if (strncmp(bname, PLUGIN_PREFIX, sizeof(PLUGIN_PREFIX) - 1) != 0) {
132
0
        flb_error("[plugin] invalid plugin prefix: %s", bname);
133
0
        return NULL;
134
0
    }
135
136
0
    if (strncmp(bname + len - (sizeof(PLUGIN_EXTENSION) - 1),
137
0
                PLUGIN_EXTENSION, sizeof(PLUGIN_EXTENSION) - 1) != 0) {
138
0
        flb_error("[plugin] invalid plugin extension: %s", bname);
139
0
        return NULL;
140
0
    }
141
142
    /* Get the expected structure name */
143
0
    name = flb_malloc(len + (sizeof(PLUGIN_STRUCT_SUFFIX) - 1) + 1);
144
0
    if (!name) {
145
0
        flb_errno();
146
0
        return NULL;
147
0
    }
148
149
    /* Name without prefix */
150
0
    p = bname + (sizeof(PLUGIN_PREFIX) - 1);
151
0
    o_len = len - (sizeof(PLUGIN_PREFIX) - 1) - (sizeof(PLUGIN_EXTENSION) - 1);
152
0
    memcpy(name, p, o_len);
153
0
    name[o_len] = '\0';
154
155
    /* Validate expected plugin type */
156
0
    if (is_input(name) == FLB_FALSE &&
157
0
        is_processor(name) == FLB_FALSE &&
158
0
        is_filter(name) == FLB_FALSE &&
159
0
        is_output(name) == FLB_FALSE) {
160
0
        flb_error("[plugin] invalid plugin type: %s", name);
161
0
        flb_free(name);
162
0
        return NULL;
163
0
    }
164
165
    /* Append struct suffix */
166
0
    p = name + o_len;
167
0
    memcpy(p, PLUGIN_STRUCT_SUFFIX, sizeof(PLUGIN_STRUCT_SUFFIX) - 1);
168
0
    o_len += sizeof(PLUGIN_STRUCT_SUFFIX) - 1;
169
0
    name[o_len] = '\0';
170
171
0
    return name;
172
0
}
173
174
static void destroy_plugin(struct flb_plugin *plugin)
175
0
{
176
0
    flb_sds_destroy(plugin->path);
177
0
    dlclose(plugin->dso_handle);
178
0
    mk_list_del(&plugin->_head);
179
0
    flb_free(plugin);
180
0
}
181
182
/* Creates the global plugin context for 'dynamic plugins' */
183
struct flb_plugins *flb_plugin_create()
184
87.4k
{
185
87.4k
    struct flb_plugins *ctx;
186
187
87.4k
    ctx = flb_malloc(sizeof(struct flb_plugins));
188
87.4k
    if (!ctx) {
189
21
        flb_errno();
190
21
        return NULL;
191
21
    }
192
193
87.4k
    mk_list_init(&ctx->input);
194
87.4k
    mk_list_init(&ctx->processor);
195
87.4k
    mk_list_init(&ctx->filter);
196
87.4k
    mk_list_init(&ctx->output);
197
198
87.4k
    return ctx;
199
87.4k
}
200
201
int flb_plugin_load(char *path, struct flb_plugins *ctx,
202
                    struct flb_config *config)
203
0
{
204
0
    int type = -1;
205
0
    void *dso_handle;
206
0
    void *symbol = NULL;
207
0
    char *plugin_stname;
208
0
    struct flb_plugin *plugin;
209
0
    struct flb_input_plugin *input;
210
0
    struct flb_processor_plugin *processor;
211
0
    struct flb_filter_plugin *filter;
212
0
    struct flb_output_plugin *output;
213
214
    /* Open the shared object file: dlopen(3) */
215
0
    dso_handle = get_handle(path);
216
0
    if (!dso_handle) {
217
0
        return -1;
218
0
    }
219
220
    /*
221
     * Based on the shared object file name, compose the expected
222
     * registration structure name.
223
     */
224
0
    plugin_stname = path_to_plugin_name(path);
225
0
    if (!plugin_stname) {
226
0
        dlclose(dso_handle);
227
0
        return -1;
228
0
    }
229
230
    /* Get the registration structure */
231
0
    symbol = load_symbol(dso_handle, plugin_stname);
232
0
    if (!symbol) {
233
0
        flb_error("[plugin] cannot load plugin '%s', "
234
0
                  "registration structure is missing '%s'",
235
0
                  path, plugin_stname);
236
0
        flb_free(plugin_stname);
237
0
        dlclose(dso_handle);
238
0
        return -1;
239
0
    }
240
241
    /* Detect plugin type and link it to the main context */
242
0
    if (is_input(plugin_stname) == FLB_TRUE) {
243
0
        type = FLB_PLUGIN_INPUT;
244
0
        input = flb_malloc(sizeof(struct flb_input_plugin));
245
0
        if (!input) {
246
0
            flb_errno();
247
0
            flb_free(plugin_stname);
248
0
            dlclose(dso_handle);
249
0
            return -1;
250
0
        }
251
0
        memcpy(input, symbol, sizeof(struct flb_input_plugin));
252
0
        mk_list_add(&input->_head, &config->in_plugins);
253
0
    }
254
0
    else if (is_processor(plugin_stname) == FLB_TRUE) {
255
0
        type = FLB_PLUGIN_PROCESSOR;
256
0
        processor = flb_calloc(1, sizeof(struct flb_processor_plugin));
257
0
        if (processor == NULL) {
258
0
            flb_errno();
259
0
            flb_free(plugin_stname);
260
0
            dlclose(dso_handle);
261
0
            return -1;
262
0
        }
263
        /* Old descriptors end before cb_preflight. Do not read their trailing
264
         * memory; flags in the stable prefix opt into the new extension. */
265
0
        memcpy(processor, symbol, offsetof(struct flb_processor_plugin, cb_preflight));
266
0
        if (processor->flags & FLB_PROCESSOR_FLAG_PREFLIGHT) {
267
0
            memcpy(&processor->cb_preflight,
268
0
                   (char *) symbol + offsetof(struct flb_processor_plugin, cb_preflight),
269
0
                   sizeof(processor->cb_preflight));
270
0
        }
271
0
        if (processor->flags & FLB_PROCESSOR_FLAG_INPUT_STOP) {
272
0
            memcpy(&processor->cb_input_stop,
273
0
                   (char *) symbol + offsetof(struct flb_processor_plugin, cb_input_stop),
274
0
                   sizeof(processor->cb_input_stop));
275
0
        }
276
0
        mk_list_add(&processor->_head, &config->processor_plugins);
277
0
    }
278
0
    else if (is_filter(plugin_stname) == FLB_TRUE) {
279
0
        type = FLB_PLUGIN_FILTER;
280
0
        filter = flb_malloc(sizeof(struct flb_filter_plugin));
281
0
        if (!filter) {
282
0
            flb_errno();
283
0
            flb_free(plugin_stname);
284
0
            dlclose(dso_handle);
285
0
            return -1;
286
0
        }
287
0
        memcpy(filter, symbol, sizeof(struct flb_filter_plugin));
288
0
        mk_list_add(&filter->_head, &config->filter_plugins);
289
0
    }
290
0
    else if (is_output(plugin_stname) == FLB_TRUE) {
291
0
        type = FLB_PLUGIN_OUTPUT;
292
0
        output = flb_malloc(sizeof(struct flb_output_plugin));
293
0
        if (!output) {
294
0
            flb_errno();
295
0
            flb_free(plugin_stname);
296
0
            dlclose(dso_handle);
297
0
            return -1;
298
0
        }
299
0
        memcpy(output, symbol, sizeof(struct flb_output_plugin));
300
0
        mk_list_add(&output->_head, &config->out_plugins);
301
0
    }
302
0
    flb_free(plugin_stname);
303
304
0
    if (type == -1) {
305
0
        flb_error("[plugin] plugin type not defined on '%s'", path);
306
0
        dlclose(dso_handle);
307
0
        return -1;
308
0
    }
309
310
    /* Create plugin context (internal reference only) */
311
0
    plugin = flb_malloc(sizeof(struct flb_plugin));
312
0
    if (!plugin) {
313
0
        flb_errno();
314
0
        dlclose(dso_handle);
315
0
        return -1;
316
0
    }
317
318
0
    plugin->type = type;
319
0
    plugin->path = flb_sds_create(path);
320
0
    plugin->dso_handle = dso_handle;
321
322
    /* Link by type to the plugins parent context */
323
0
    if (type == FLB_PLUGIN_INPUT) {
324
0
        mk_list_add(&plugin->_head, &ctx->input);
325
0
    }
326
0
    else if (type == FLB_PLUGIN_PROCESSOR) {
327
0
        mk_list_add(&plugin->_head, &ctx->processor);
328
0
    }
329
0
    else if (type == FLB_PLUGIN_FILTER) {
330
0
        mk_list_add(&plugin->_head, &ctx->filter);
331
0
    }
332
0
    else if (type == FLB_PLUGIN_OUTPUT) {
333
0
        mk_list_add(&plugin->_head, &ctx->output);
334
0
    }
335
336
0
    return 0;
337
0
}
338
339
int flb_plugin_load_router(char *path, struct flb_config *config)
340
0
{
341
0
    int ret = -1;
342
0
    char *bname;
343
344
0
    bname = basename(path);
345
346
    /* Is this a DSO C plugin ? */
347
0
    if (strncmp(bname, PLUGIN_PREFIX, sizeof(PLUGIN_PREFIX) - 1) == 0) {
348
0
        ret = flb_plugin_load(path, config->dso_plugins, config);
349
0
        if (ret == -1) {
350
0
            flb_error("[plugin] error loading DSO C plugin: %s", path);
351
0
            return -1;
352
0
        }
353
0
    }
354
0
    else {
355
0
#ifdef FLB_HAVE_PROXY_GO
356
0
        if (flb_plugin_proxy_create(path, 0, config) == NULL) {
357
0
            flb_error("[plugin] error loading proxy plugin: %s", path);
358
0
            return -1;
359
0
        }
360
#else
361
        flb_error("[plugin] unsupported plugin type at: %s", path);
362
        return -1;
363
#endif
364
0
    }
365
366
0
    return 0;
367
0
}
368
369
int flb_plugin_load_config_format(struct flb_cf *cf, struct flb_config *config)
370
0
{
371
0
    int ret;
372
0
    struct mk_list *head;
373
0
    struct cfl_list *head_e;
374
0
    struct flb_cf_section *section;
375
0
    struct cfl_kvpair *entry;
376
377
    /* read all 'plugins' sections */
378
0
    mk_list_foreach(head, &cf->plugins) {
379
0
        section = mk_list_entry(head, struct flb_cf_section, _head_section);
380
381
0
        cfl_list_foreach(head_e, &section->properties->list) {
382
0
            entry = cfl_list_entry(head_e, struct cfl_kvpair, _head);
383
384
            /* Load plugin with router function */
385
0
            ret = flb_plugin_load_router(entry->key, config);
386
0
            if (ret == -1) {
387
0
                flb_cf_destroy(cf);
388
0
                return -1;
389
0
            }
390
0
        }
391
0
    }
392
393
0
    return 0;
394
0
}
395
396
/* Load plugins from a configuration file */
397
int flb_plugin_load_config_file(const char *file, struct flb_config *config)
398
104
{
399
104
    int ret;
400
104
    char tmp[PATH_MAX + 1];
401
104
    char *cfg = NULL;
402
104
    struct mk_list *head;
403
104
    struct cfl_list *head_e;
404
104
    struct flb_cf *cf;
405
104
    struct flb_cf_section *section;
406
104
    struct cfl_kvpair *entry;
407
408
104
#ifndef FLB_HAVE_STATIC_CONF
409
104
    cfg = (char *) flb_utils_resolve_config_path(file, config->conf_path,
410
104
                                                 tmp, sizeof(tmp));
411
104
    if (!cfg) {
412
0
        flb_utils_error(FLB_ERR_CFG_PLUGIN_FILE);
413
0
        return -1;
414
0
    }
415
416
104
    flb_debug("[plugin] opening configuration file %s", cfg);
417
418
104
    cf = flb_cf_create_from_file(NULL, cfg);
419
#else
420
    cf = flb_config_static_open(file);
421
#endif
422
423
104
    if (!cf) {
424
101
        return -1;
425
101
    }
426
427
3
    if (cf->format == FLB_CF_FLUENTBIT) {
428
        /* (classic mode) read all 'plugins' sections */
429
3
        mk_list_foreach(head, &cf->sections) {
430
0
            section = mk_list_entry(head, struct flb_cf_section, _head);
431
0
            if (strcasecmp(section->name, "plugins") != 0) {
432
0
                continue;
433
0
            }
434
435
0
            cfl_list_foreach(head_e, &section->properties->list) {
436
0
                entry = cfl_list_entry(head_e, struct cfl_kvpair, _head);
437
0
                if (strcasecmp(entry->key, "path") != 0) {
438
0
                    continue;
439
0
                }
440
441
                /* Load plugin with router function */
442
0
                ret = flb_plugin_load_router(entry->val->data.as_string, config);
443
0
                if (ret == -1) {
444
0
                    flb_cf_destroy(cf);
445
0
                    return -1;
446
0
                }
447
0
            }
448
0
        }
449
3
    }
450
0
#ifdef FLB_HAVE_LIBYAML
451
0
    else if (cf->format == FLB_CF_YAML) {
452
        /*
453
         * pass to the config_format loader also in case some Yaml have been included in
454
         * the service section through the option 'plugins_file'
455
         */
456
0
        ret = flb_plugin_load_config_format(cf, config);
457
0
        if (ret == -1) {
458
0
            return -1;
459
0
        }
460
0
    }
461
3
#endif
462
463
3
    flb_cf_destroy(cf);
464
3
    return 0;
465
3
}
466
467
/* Destroy plugin context */
468
void flb_plugin_destroy(struct flb_plugins *ctx)
469
87.4k
{
470
87.4k
    struct mk_list *tmp;
471
87.4k
    struct mk_list *head;
472
87.4k
    struct flb_plugin *plugin;
473
474
87.4k
    mk_list_foreach_safe(head, tmp, &ctx->input) {
475
0
        plugin = mk_list_entry(head, struct flb_plugin, _head);
476
0
        destroy_plugin(plugin);
477
0
    }
478
479
87.4k
    mk_list_foreach_safe(head, tmp, &ctx->processor) {
480
0
        plugin = mk_list_entry(head, struct flb_plugin, _head);
481
0
        destroy_plugin(plugin);
482
0
    }
483
484
87.4k
    mk_list_foreach_safe(head, tmp, &ctx->filter) {
485
0
        plugin = mk_list_entry(head, struct flb_plugin, _head);
486
0
        destroy_plugin(plugin);
487
0
    }
488
489
87.4k
    mk_list_foreach_safe(head, tmp, &ctx->output) {
490
0
        plugin = mk_list_entry(head, struct flb_plugin, _head);
491
0
        destroy_plugin(plugin);
492
0
    }
493
494
87.4k
    flb_free(ctx);
495
87.4k
}