Coverage Report

Created: 2026-06-07 07:09

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/fluent-bit/plugins/in_dummy/in_dummy.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 <stdio.h>
21
#include <stdlib.h>
22
#include <time.h>
23
24
#include <msgpack.h>
25
#include <fluent-bit/flb_input.h>
26
#include <fluent-bit/flb_input_plugin.h>
27
#include <fluent-bit/flb_config.h>
28
#include <fluent-bit/flb_config_map.h>
29
#include <fluent-bit/flb_error.h>
30
#include <fluent-bit/flb_time.h>
31
#include <fluent-bit/flb_pack.h>
32
#include <fluent-bit/flb_log_event.h>
33
#include <fluent-bit/flb_utils.h>
34
#include <fluent-bit/flb_env.h>
35
36
#include "in_dummy.h"
37
38
static void generate_timestamp(struct flb_dummy *ctx,
39
                               struct flb_time *result)
40
0
{
41
0
    struct flb_time current_timestamp;
42
0
    struct flb_time delta;
43
44
0
    if (ctx->fixed_timestamp) {
45
0
        if (ctx->dummy_timestamp_set) {
46
0
            flb_time_copy(result, &ctx->dummy_timestamp);
47
0
        }
48
0
        else {
49
0
            flb_time_copy(result, &ctx->base_timestamp);
50
0
        }
51
0
    }
52
0
    else {
53
0
        if (ctx->dummy_timestamp_set) {
54
0
            flb_time_zero(&delta);
55
56
0
            flb_time_get(&current_timestamp);
57
58
0
            flb_time_diff(&current_timestamp,
59
0
                          &ctx->base_timestamp,
60
0
                          &delta);
61
62
0
            flb_time_add(&ctx->dummy_timestamp,
63
0
                         &delta,
64
0
                         result);
65
0
        }
66
0
        else {
67
0
            flb_time_get(result);
68
0
        }
69
0
    }
70
0
}
71
72
/* Fetch a raw template string from the configuration file without
73
 * performing environment variable expansion. This is required for
74
 * dynamic environment variables that may refresh at runtime. */
75
static int generate_event(struct flb_dummy *ctx)
76
0
{
77
0
    size_t           chunk_offset;
78
0
    size_t           body_length;
79
0
    char            *body_buffer;
80
0
    size_t           body_start;
81
0
    struct flb_time  timestamp;
82
0
    msgpack_unpacked object;
83
0
    int              result;
84
0
    flb_sds_t        resolved_body;
85
0
    flb_sds_t        resolved_metadata;
86
0
    char            *body_msgpack = NULL;
87
0
    char            *metadata_msgpack = NULL;
88
0
    size_t           body_msgpack_size = 0;
89
0
    size_t           metadata_msgpack_size = 0;
90
0
    int              root_type;
91
92
0
    result = FLB_EVENT_ENCODER_SUCCESS;
93
0
    body_start = 0;
94
0
    chunk_offset = 0;
95
96
0
    generate_timestamp(ctx, &timestamp);
97
98
0
    body_msgpack = ctx->ref_body_msgpack;
99
0
    body_msgpack_size = ctx->ref_body_msgpack_size;
100
0
    metadata_msgpack = ctx->ref_metadata_msgpack;
101
0
    metadata_msgpack_size = ctx->ref_metadata_msgpack_size;
102
103
0
    resolved_body = flb_config_map_translate_dynamic(ctx->ins->config, ctx->cm_body);
104
0
    if (resolved_body && flb_sds_len(resolved_body) > 0) {
105
0
        result = flb_pack_json(resolved_body,
106
0
                               flb_sds_len(resolved_body),
107
0
                               &body_msgpack,
108
0
                               &body_msgpack_size,
109
0
                               &root_type,
110
0
                               NULL);
111
0
        if (result != 0) {
112
0
            flb_plg_warn(ctx->ins, "failed to parse JSON template, using cached body");
113
0
            if (body_msgpack && body_msgpack != ctx->ref_body_msgpack) {
114
0
                flb_free(body_msgpack);
115
0
            }
116
0
            body_msgpack = ctx->ref_body_msgpack;
117
0
            body_msgpack_size = ctx->ref_body_msgpack_size;
118
0
            result = 0;
119
0
        }
120
0
    }
121
122
0
    resolved_metadata = flb_config_map_translate_dynamic(ctx->ins->config, ctx->cm_metadata);
123
0
    if (resolved_metadata && flb_sds_len(resolved_metadata) > 0) {
124
0
        result = flb_pack_json(resolved_metadata,
125
0
                               flb_sds_len(resolved_metadata),
126
0
                               &metadata_msgpack,
127
0
                               &metadata_msgpack_size,
128
0
                               &root_type,
129
0
                               NULL);
130
0
        if (result != 0) {
131
0
            flb_plg_warn(ctx->ins, "failed to parse JSON template, using cached metadata");
132
0
            if (metadata_msgpack && metadata_msgpack != ctx->ref_metadata_msgpack) {
133
0
                flb_free(metadata_msgpack);
134
0
            }
135
0
            metadata_msgpack = ctx->ref_metadata_msgpack;
136
0
            metadata_msgpack_size = ctx->ref_metadata_msgpack_size;
137
0
            result = 0;
138
0
        }
139
0
    }
140
141
0
    msgpack_unpacked_init(&object);
142
143
0
    while (result == FLB_EVENT_ENCODER_SUCCESS &&
144
0
           msgpack_unpack_next(&object,
145
0
                               body_msgpack,
146
0
                               body_msgpack_size,
147
0
                               &chunk_offset) == MSGPACK_UNPACK_SUCCESS) {
148
0
        body_buffer = &body_msgpack[body_start];
149
0
        body_length = chunk_offset - body_start;
150
151
0
        if (object.data.type == MSGPACK_OBJECT_MAP) {
152
0
            flb_log_event_encoder_begin_record(ctx->encoder);
153
154
0
            flb_log_event_encoder_set_timestamp(ctx->encoder, &timestamp);
155
156
0
            result = flb_log_event_encoder_set_metadata_from_raw_msgpack(
157
0
                        ctx->encoder,
158
0
                        metadata_msgpack,
159
0
                        metadata_msgpack_size);
160
161
0
            if (result == FLB_EVENT_ENCODER_SUCCESS) {
162
0
                result = flb_log_event_encoder_set_body_from_raw_msgpack(
163
0
                            ctx->encoder,
164
0
                            body_buffer,
165
0
                            body_length);
166
0
            }
167
168
0
            if (result == FLB_EVENT_ENCODER_SUCCESS) {
169
0
                result = flb_log_event_encoder_commit_record(ctx->encoder);
170
0
            }
171
0
        }
172
173
0
        body_start = chunk_offset;
174
0
    }
175
176
0
    msgpack_unpacked_destroy(&object);
177
178
    /* Clean up */
179
0
    if (resolved_body) {
180
0
        flb_sds_destroy(resolved_body);
181
0
    }
182
0
    if (resolved_metadata) {
183
0
        flb_sds_destroy(resolved_metadata);
184
0
    }
185
    /* Only free msgpack if we allocated it (not using cached) */
186
0
    if (body_msgpack && body_msgpack != ctx->ref_body_msgpack) {
187
0
        flb_free(body_msgpack);
188
0
    }
189
0
    if (metadata_msgpack && metadata_msgpack != ctx->ref_metadata_msgpack) {
190
0
        flb_free(metadata_msgpack);
191
0
    }
192
193
0
    if (result == FLB_EVENT_ENCODER_SUCCESS) {
194
0
        result = 0;
195
0
    }
196
0
    else {
197
0
        result = -1;
198
0
    }
199
200
0
    return result;
201
0
}
202
203
/* cb_collect callback */
204
static int in_dummy_collect(struct flb_input_instance *ins,
205
                            struct flb_config *config,
206
                            void *in_context)
207
0
{
208
0
    int               result;
209
0
    int               index;
210
0
    struct flb_dummy *ctx;
211
212
0
    ctx = (struct flb_dummy *) in_context;
213
214
0
    if (ctx->samples > 0 && (ctx->samples_count >= ctx->samples)) {
215
0
        return -1;
216
0
    }
217
218
0
    result = 0;
219
220
0
    if (ctx->samples_count == 0 || !ctx->fixed_timestamp) {
221
0
        flb_log_event_encoder_reset(ctx->encoder);
222
223
0
        for (index = 0 ; index < ctx->copies && result == 0 ; index++) {
224
0
            result = generate_event(ctx);
225
0
        }
226
0
    }
227
228
0
    if (result == 0) {
229
0
        if (ctx->encoder->output_length > 0) {
230
0
            flb_input_log_append(ins, NULL, 0,
231
0
                                 ctx->encoder->output_buffer,
232
0
                                 ctx->encoder->output_length);
233
0
        }
234
0
        else {
235
0
            flb_plg_error(ins, "log chunk size == 0");
236
0
        }
237
0
    }
238
0
    else {
239
0
        flb_plg_error(ins, "log chunk genartion error (%d)", result);
240
0
    }
241
242
0
    if (ctx->samples > 0) {
243
0
        ctx->samples_count++;
244
0
    }
245
246
0
    return 0;
247
0
}
248
249
static int config_destroy(struct flb_dummy *ctx)
250
0
{
251
0
    if (ctx->ref_body_msgpack != NULL) {
252
0
        flb_free(ctx->ref_body_msgpack);
253
0
    }
254
255
0
    if (ctx->ref_metadata_msgpack != NULL) {
256
0
        flb_free(ctx->ref_metadata_msgpack);
257
0
    }
258
259
260
261
0
    if (ctx->encoder != NULL) {
262
0
        flb_log_event_encoder_destroy(ctx->encoder);
263
0
    }
264
265
0
    flb_free(ctx);
266
267
0
    return 0;
268
0
}
269
270
/* Set plugin configuration */
271
static int configure(struct flb_dummy *ctx,
272
                     struct flb_input_instance *in,
273
                     struct timespec *tm)
274
0
{
275
0
    int ret = -1;
276
0
    int root_type;
277
0
    flb_sds_t resolved_msg = NULL;
278
279
0
    ctx->ref_metadata_msgpack = NULL;
280
0
    ctx->ref_body_msgpack = NULL;
281
0
    ctx->dummy_timestamp_set = FLB_FALSE;
282
283
0
    ret = flb_input_config_map_set(in, (void *) ctx);
284
0
    if (ret == -1) {
285
0
        return -1;
286
0
    }
287
288
    /* interval settings */
289
0
    if (ctx->interval_sec < 0 || ctx->interval_nsec < 0) {
290
        /* Illegal settings. Override them. */
291
0
        ctx->interval_sec = atoi(DEFAULT_INTERVAL_SEC);
292
0
        ctx->interval_nsec = atoi(DEFAULT_INTERVAL_NSEC);
293
0
    }
294
295
    /* default settings */
296
0
    tm->tv_sec  = 1;
297
0
    tm->tv_nsec = 0;
298
299
0
    if (ctx->interval_sec > 0 || ctx->interval_nsec > 0) {
300
        /* Set using interval settings. */
301
0
        tm->tv_sec  = ctx->interval_sec;
302
0
        tm->tv_nsec = ctx->interval_nsec;
303
0
    }
304
0
    else {
305
0
        if (ctx->rate > 1) {
306
            /* Set using rate settings. */
307
0
            tm->tv_sec = 0;
308
0
            tm->tv_nsec = 1000000000 / ctx->rate;
309
0
        }
310
0
    }
311
312
    /* dummy timestamp */
313
0
    flb_time_zero(&ctx->dummy_timestamp);
314
315
0
    if (ctx->start_time_sec >= 0 || ctx->start_time_nsec >= 0) {
316
0
        ctx->dummy_timestamp_set = FLB_TRUE;
317
318
0
        if (ctx->start_time_sec >= 0) {
319
0
            ctx->dummy_timestamp.tm.tv_sec = ctx->start_time_sec;
320
0
        }
321
0
        if (ctx->start_time_nsec >= 0) {
322
0
            ctx->dummy_timestamp.tm.tv_nsec = ctx->start_time_nsec;
323
0
        }
324
0
    }
325
326
0
    flb_time_get(&ctx->base_timestamp);
327
328
    /* Locate config map entries for templates */
329
0
    ctx->cm_body = flb_config_map_find(in->config_map, "dummy");
330
0
    ctx->cm_metadata = flb_config_map_find(in->config_map, "metadata");
331
332
    /* Validate the body template by parsing it once */
333
0
    resolved_msg = flb_config_map_translate_dynamic(in->config, ctx->cm_body);
334
0
    if (!resolved_msg || flb_sds_len(resolved_msg) == 0) {
335
0
        if (resolved_msg) {
336
0
            flb_sds_destroy(resolved_msg);
337
0
        }
338
0
        flb_plg_warn(ctx->ins, "environment variable resolution failed for dummy message, using default");
339
0
        resolved_msg = flb_sds_create(DEFAULT_DUMMY_MESSAGE);
340
0
        if (!resolved_msg) {
341
0
            flb_plg_error(ctx->ins, "failed to create default body template");
342
0
            return -1;
343
0
        }
344
0
    }
345
346
0
    ret = flb_pack_json(resolved_msg,
347
0
                        flb_sds_len(resolved_msg),
348
0
                        &ctx->ref_body_msgpack,
349
0
                        &ctx->ref_body_msgpack_size,
350
0
                        &root_type,
351
0
                        NULL);
352
353
0
    if (ret != 0) {
354
0
        flb_plg_warn(ctx->ins, "data is incomplete. Use default string.");
355
356
0
        ret = flb_pack_json(DEFAULT_DUMMY_MESSAGE,
357
0
                            strlen(DEFAULT_DUMMY_MESSAGE),
358
0
                            &ctx->ref_body_msgpack,
359
0
                            &ctx->ref_body_msgpack_size,
360
0
                            &root_type,
361
0
                            NULL);
362
0
        if (ret != 0) {
363
0
            flb_plg_error(ctx->ins, "unexpected error");
364
0
            flb_sds_destroy(resolved_msg);
365
0
            return -1;
366
0
        }
367
0
    }
368
369
0
    flb_sds_destroy(resolved_msg);
370
371
    /* Validate the metadata template by parsing it once */
372
0
    resolved_msg = flb_config_map_translate_dynamic(in->config, ctx->cm_metadata);
373
0
    if (!resolved_msg || flb_sds_len(resolved_msg) == 0) {
374
0
        if (resolved_msg) {
375
0
            flb_sds_destroy(resolved_msg);
376
0
        }
377
0
        flb_plg_warn(ctx->ins, "environment variable resolution failed for metadata, using default");
378
0
        resolved_msg = flb_sds_create(DEFAULT_DUMMY_METADATA);
379
0
    }
380
381
0
    ret = flb_pack_json(resolved_msg,
382
0
                        flb_sds_len(resolved_msg),
383
0
                        &ctx->ref_metadata_msgpack,
384
0
                        &ctx->ref_metadata_msgpack_size,
385
0
                        &root_type,
386
0
                        NULL);
387
388
0
    if (ret != 0) {
389
0
        flb_plg_warn(ctx->ins, "data is incomplete. Use default string.");
390
391
0
        ret = flb_pack_json(DEFAULT_DUMMY_METADATA,
392
0
                            strlen(DEFAULT_DUMMY_METADATA),
393
0
                            &ctx->ref_metadata_msgpack,
394
0
                            &ctx->ref_metadata_msgpack_size,
395
0
                            &root_type,
396
0
                            NULL);
397
398
0
        if (ret != 0) {
399
0
            flb_plg_error(ctx->ins, "unexpected error");
400
0
            flb_sds_destroy(resolved_msg);
401
0
            return -1;
402
0
        }
403
0
    }
404
405
0
    flb_sds_destroy(resolved_msg);
406
407
0
    return 0;
408
0
}
409
410
411
412
413
/* Initialize plugin */
414
static int in_dummy_init(struct flb_input_instance *in,
415
                         struct flb_config *config, void *data)
416
0
{
417
0
    int ret = -1;
418
0
    struct flb_dummy *ctx = NULL;
419
0
    struct timespec tm;
420
421
    /* Allocate space for the configuration */
422
0
    ctx = flb_malloc(sizeof(struct flb_dummy));
423
0
    if (ctx == NULL) {
424
0
        flb_errno();
425
0
        return -1;
426
0
    }
427
0
    ctx->ins = in;
428
0
    ctx->samples = 0;
429
0
    ctx->samples_count = 0;
430
0
    ctx->test_hang_on_exit = FLB_FALSE;
431
432
    /* Initialize head config */
433
0
    ret = configure(ctx, in, &tm);
434
0
    if (ret < 0) {
435
0
        config_destroy(ctx);
436
0
        return -1;
437
0
    }
438
439
0
    ctx->encoder = flb_log_event_encoder_create(FLB_LOG_EVENT_FORMAT_DEFAULT);
440
0
    if (ctx->encoder == NULL) {
441
0
        flb_plg_error(in, "could not initialize event encoder");
442
0
        config_destroy(ctx);
443
444
0
        return -1;
445
0
    }
446
447
0
    flb_input_set_context(in, ctx);
448
449
0
    ret = flb_input_set_collector_time(in,
450
0
                                       in_dummy_collect,
451
0
                                       tm.tv_sec,
452
0
                                       tm.tv_nsec, config);
453
0
    if (ret < 0) {
454
0
        flb_plg_error(ctx->ins, "could not set collector for dummy input plugin");
455
0
        config_destroy(ctx);
456
0
        return -1;
457
0
    }
458
459
0
    ctx->coll_fd = ret;
460
461
0
    flb_time_get(&ctx->base_timestamp);
462
463
0
    return 0;
464
0
}
465
466
static int in_dummy_pre_run(struct flb_input_instance *in,
467
                            struct flb_config *config, void *in_context)
468
0
{
469
0
    struct flb_dummy *ctx;
470
471
0
    ctx = (struct flb_dummy *) in_context;
472
473
0
    if (ctx != NULL && ctx->flush_on_startup) {
474
0
        in_dummy_collect(in, config, in_context);
475
0
    }
476
477
0
    return 0;
478
0
}
479
480
static void in_dummy_pause(void *data, struct flb_config *config)
481
0
{
482
0
    struct flb_dummy *ctx = data;
483
484
0
    flb_input_collector_pause(ctx->coll_fd, ctx->ins);
485
0
}
486
487
static void in_dummy_resume(void *data, struct flb_config *config)
488
0
{
489
0
    struct flb_dummy *ctx = data;
490
491
0
    flb_input_collector_resume(ctx->coll_fd, ctx->ins);
492
0
}
493
494
static int in_dummy_exit(void *data, struct flb_config *config)
495
0
{
496
0
    (void) *config;
497
0
    struct flb_dummy *ctx = data;
498
499
    /* Test-only hang used by watchdog tests */
500
0
    if (ctx->test_hang_on_exit) {
501
0
        flb_plg_debug(ctx->ins, "TEST: Simulating hang for hot reload watchdog test");
502
        /* 1000 seconds */
503
0
        flb_time_msleep(1000 * 1000);
504
0
    }
505
506
0
    config_destroy(ctx);
507
508
0
    return 0;
509
0
}
510
511
/* Configuration properties map */
512
static struct flb_config_map config_map[] = {
513
   {
514
    FLB_CONFIG_MAP_INT, "samples", "0",
515
    0, FLB_TRUE, offsetof(struct flb_dummy, samples),
516
    "set a number of times to generate event."
517
   },
518
   {
519
    FLB_CONFIG_MAP_STR, "dummy", DEFAULT_DUMMY_MESSAGE,
520
    FLB_CONFIG_MAP_DYNAMIC_ENV, FLB_FALSE, 0,
521
    "set the sample record to be generated. It should be a JSON object."
522
    "Environment variables are resolved dynamically."
523
   },
524
   {
525
    FLB_CONFIG_MAP_STR, "metadata", DEFAULT_DUMMY_METADATA,
526
    FLB_CONFIG_MAP_DYNAMIC_ENV, FLB_FALSE, 0,
527
    "set the sample metadata to be generated. It should be a JSON object."
528
    "Environment variables are resolved dynamically."
529
   },
530
   {
531
    FLB_CONFIG_MAP_INT, "rate", DEFAULT_RATE,
532
    0, FLB_TRUE, offsetof(struct flb_dummy, rate),
533
    "set a number of events per second."
534
   },
535
   {
536
    FLB_CONFIG_MAP_INT, "interval_sec", DEFAULT_INTERVAL_SEC,
537
    0, FLB_TRUE, offsetof(struct flb_dummy, interval_sec),
538
    "set seconds of interval to generate events. overrides rate setting."
539
   },
540
   {
541
    FLB_CONFIG_MAP_INT, "interval_nsec", DEFAULT_INTERVAL_NSEC,
542
    0, FLB_TRUE, offsetof(struct flb_dummy, interval_nsec),
543
    "set nanoseconds of interval to generate events. overrides rate setting."
544
   },
545
   {
546
    FLB_CONFIG_MAP_INT, "copies", "1",
547
    0, FLB_TRUE, offsetof(struct flb_dummy, copies),
548
    "set the number of copies to generate per collectd."
549
   },
550
   {
551
    FLB_CONFIG_MAP_INT, "start_time_sec", "-1",
552
    0, FLB_TRUE, offsetof(struct flb_dummy, start_time_sec),
553
    "set a dummy base timestamp in seconds."
554
   },
555
   {
556
    FLB_CONFIG_MAP_INT, "start_time_nsec", "-1",
557
    0, FLB_TRUE, offsetof(struct flb_dummy, start_time_nsec),
558
    "set a dummy base timestamp in nanoseconds."
559
   },
560
   {
561
    FLB_CONFIG_MAP_BOOL, "fixed_timestamp", "off",
562
    0, FLB_TRUE, offsetof(struct flb_dummy, fixed_timestamp),
563
    "used a fixed timestamp, allows the message to pre-generated once."
564
   },
565
   {
566
    FLB_CONFIG_MAP_BOOL, "flush_on_startup", "false",
567
    0, FLB_TRUE, offsetof(struct flb_dummy, flush_on_startup),
568
    "generate the first event on startup"
569
   },
570
   {
571
    FLB_CONFIG_MAP_BOOL, "test_hang_on_exit", "false",
572
    0, FLB_TRUE, offsetof(struct flb_dummy, test_hang_on_exit),
573
    "TEST ONLY: simulate hang during exit to test hot reload watchdog"
574
   },
575
   {0}
576
};
577
578
579
struct flb_input_plugin in_dummy_plugin = {
580
    .name         = "dummy",
581
    .description  = "Generate dummy data",
582
    .cb_init      = in_dummy_init,
583
    .cb_pre_run   = in_dummy_pre_run,
584
    .cb_collect   = in_dummy_collect,
585
    .cb_flush_buf = NULL,
586
    .config_map   = config_map,
587
    .cb_pause     = in_dummy_pause,
588
    .cb_resume    = in_dummy_resume,
589
    .cb_exit      = in_dummy_exit
590
};