/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, §ion->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, §ion->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 | } |