Coverage Report

Created: 2025-11-07 08:14

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