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