Coverage Report

Created: 2026-03-22 07:09

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/fluent-bit/src/flb_conditionals.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_info.h>
21
#include <fluent-bit/flb_mem.h>
22
#include <fluent-bit/flb_error.h>
23
#include <fluent-bit/flb_sds.h>
24
#include <fluent-bit/flb_regex.h>
25
#include <fluent-bit/flb_mp.h>
26
#include <fluent-bit/flb_conditionals.h>
27
28
static struct cfl_variant *default_get_record_variant(struct flb_condition_rule *rule,
29
                                                      void *ctx)
30
0
{
31
0
    struct flb_mp_chunk_record *record = (struct flb_mp_chunk_record *) ctx;
32
33
0
    if (!record || !rule) {
34
0
        return NULL;
35
0
    }
36
37
0
    switch (rule->context) {
38
0
    case RECORD_CONTEXT_METADATA:
39
0
        if (record->cobj_metadata) {
40
0
            return record->cobj_metadata->variant;
41
0
        }
42
0
        break;
43
44
0
    case RECORD_CONTEXT_BODY:
45
0
        if (record->cobj_record) {
46
0
            return record->cobj_record->variant;
47
0
        }
48
0
        break;
49
50
0
    default:
51
0
        break;
52
0
    }
53
54
0
    return NULL;
55
0
}
56
57
static struct flb_condition_rule *rule_create(const char *field,
58
                                             enum flb_rule_operator op,
59
                                             void *value,
60
                                             int value_count,
61
                                             enum record_context_type context)
62
0
{
63
0
    struct flb_condition_rule *rule;
64
0
    int i,j;
65
66
0
    if (!field || !value) {
67
0
        return NULL;
68
0
    }
69
70
0
    switch (op) {
71
0
    case FLB_RULE_OP_EQ:
72
0
    case FLB_RULE_OP_NEQ:
73
0
    case FLB_RULE_OP_REGEX:
74
0
    case FLB_RULE_OP_NOT_REGEX:
75
0
        if (!value || !((char *)value)[0]) {
76
0
            return NULL;
77
0
        }
78
0
        break;
79
0
    case FLB_RULE_OP_GT:
80
0
    case FLB_RULE_OP_LT:
81
0
    case FLB_RULE_OP_GTE:
82
0
    case FLB_RULE_OP_LTE:
83
0
        if (!value) {
84
0
            return NULL;
85
0
        }
86
0
        break;
87
88
0
    case FLB_RULE_OP_IN:
89
0
    case FLB_RULE_OP_NOT_IN:
90
0
        if (!value || value_count <= 0 || !((char **)value)[0]) {
91
0
            return NULL;
92
0
        }
93
0
        for (i = 0; i < value_count; i++) {
94
0
            if (!((char **)value)[i]) {
95
0
                return NULL;
96
0
            }
97
0
        }
98
0
        break;
99
100
0
    default:
101
0
        return NULL;
102
0
    }
103
104
0
    rule = flb_calloc(1, sizeof(struct flb_condition_rule));
105
0
    if (!rule) {
106
0
        cfl_errno();
107
0
        return NULL;
108
0
    }
109
110
0
    rule->ra = flb_cfl_ra_create((char *)field, FLB_TRUE);
111
0
    if (!rule->ra) {
112
0
        flb_free(rule);
113
0
        return NULL;
114
0
    }
115
116
0
    rule->context = context;
117
0
    rule->op = op;
118
119
0
    switch (op) {
120
0
    case FLB_RULE_OP_NEQ:
121
0
    case FLB_RULE_OP_EQ:
122
0
        rule->value.str_val = flb_sds_create((char *)value);
123
0
        if (!rule->value.str_val) {
124
0
            flb_cfl_ra_destroy(rule->ra);
125
0
            flb_free(rule);
126
0
            return NULL;
127
0
        }
128
0
        break;
129
130
0
    case FLB_RULE_OP_GT:
131
0
    case FLB_RULE_OP_LT:
132
0
    case FLB_RULE_OP_GTE:
133
0
    case FLB_RULE_OP_LTE:
134
0
        rule->value.num_val = *(double *)value;
135
0
        break;
136
137
0
    case FLB_RULE_OP_REGEX:
138
0
    case FLB_RULE_OP_NOT_REGEX:
139
0
        rule->regex = flb_regex_create((char *)value);
140
0
        if (!rule->regex) {
141
0
            flb_cfl_ra_destroy(rule->ra);
142
0
            flb_free(rule);
143
0
            return NULL;
144
0
        }
145
0
        break;
146
147
0
    case FLB_RULE_OP_IN:
148
0
    case FLB_RULE_OP_NOT_IN:
149
0
        rule->value.array.values = flb_calloc(value_count, sizeof(flb_sds_t));
150
0
        if (!rule->value.array.values) {
151
0
            flb_cfl_ra_destroy(rule->ra);
152
0
            flb_free(rule);
153
0
            return NULL;
154
0
        }
155
156
0
        for (i = 0; i < value_count; i++) {
157
0
            rule->value.array.values[i] = flb_sds_create(((char **)value)[i]);
158
0
            if (!rule->value.array.values[i]) {
159
0
                for (j = 0; j < i; j++) {
160
0
                    flb_sds_destroy(rule->value.array.values[j]);
161
0
                }
162
0
                flb_free(rule->value.array.values);
163
0
                flb_cfl_ra_destroy(rule->ra);
164
0
                flb_free(rule);
165
0
                return NULL;
166
0
            }
167
0
        }
168
0
        rule->value.array.count = value_count;
169
0
        break;
170
0
    }
171
172
0
    return rule;
173
0
}
174
175
static void rule_destroy(struct flb_condition_rule *rule)
176
0
{
177
0
    int i;
178
179
0
    if (!rule) {
180
0
        return;
181
0
    }
182
183
0
    if (rule->ra) {
184
0
        flb_cfl_ra_destroy(rule->ra);
185
0
    }
186
187
0
    switch (rule->op) {
188
0
    case FLB_RULE_OP_EQ:
189
0
    case FLB_RULE_OP_NEQ:
190
0
        if (rule->value.str_val) {
191
0
            flb_sds_destroy(rule->value.str_val);
192
0
        }
193
0
        break;
194
195
0
    case FLB_RULE_OP_REGEX:
196
0
    case FLB_RULE_OP_NOT_REGEX:
197
0
        if (rule->regex) {
198
0
            flb_regex_destroy(rule->regex);
199
0
        }
200
0
        break;
201
202
0
    case FLB_RULE_OP_IN:
203
0
    case FLB_RULE_OP_NOT_IN:
204
0
        for (i = 0; i < rule->value.array.count; i++) {
205
0
            flb_sds_destroy(rule->value.array.values[i]);
206
0
        }
207
0
        flb_free(rule->value.array.values);
208
0
        break;
209
210
0
    case FLB_RULE_OP_GT:
211
0
    case FLB_RULE_OP_LT:
212
0
    case FLB_RULE_OP_GTE:
213
0
    case FLB_RULE_OP_LTE:
214
0
        break;
215
216
0
    default:
217
0
        break;
218
0
    }
219
220
0
    flb_free(rule);
221
0
}
222
223
struct flb_condition *flb_condition_create(enum flb_condition_operator op)
224
0
{
225
0
    struct flb_condition *cond;
226
227
0
    cond = flb_calloc(1, sizeof(struct flb_condition));
228
0
    if (!cond) {
229
0
        cfl_errno();
230
0
        return NULL;
231
0
    }
232
233
0
    cond->op = op;
234
0
    mk_list_init(&cond->rules);
235
236
0
    return cond;
237
0
}
238
239
int flb_condition_add_rule(struct flb_condition *cond,
240
                           const char *field,
241
                           enum flb_rule_operator op,
242
                           void *value,
243
                           int value_count,
244
                           enum record_context_type context)
245
0
{
246
0
    struct flb_condition_rule *rule;
247
248
0
    if (!cond || !field || !value) {
249
0
        return FLB_FALSE;
250
0
    }
251
252
0
    rule = rule_create(field, op, value, value_count, context);
253
0
    if (!rule) {
254
0
        return FLB_FALSE;
255
0
    }
256
257
0
    mk_list_add(&rule->_head, &cond->rules);
258
0
    return FLB_TRUE;
259
0
}
260
261
void flb_condition_destroy(struct flb_condition *cond)
262
0
{
263
0
    struct mk_list *tmp;
264
0
    struct mk_list *head;
265
0
    struct flb_condition_rule *rule;
266
267
0
    if (!cond) {
268
0
        return;
269
0
    }
270
271
0
    mk_list_foreach_safe(head, tmp, &cond->rules) {
272
0
        rule = mk_list_entry(head, struct flb_condition_rule, _head);
273
0
        mk_list_del(&rule->_head);
274
0
        rule_destroy(rule);
275
0
    }
276
277
0
    flb_free(cond);
278
0
}
279
280
static int evaluate_rule(struct flb_condition_rule *rule,
281
                         struct cfl_variant *record_variant)
282
0
{
283
0
    flb_sds_t str_val;
284
0
    int i;
285
0
    int result = FLB_FALSE;
286
0
    double num_val;
287
288
0
    if (!rule || !record_variant) {
289
0
        flb_trace("[condition] evaluate_rule: NULL rule or record variant");
290
0
        return FLB_FALSE;
291
0
    }
292
293
0
    flb_trace("[condition] evaluating rule with record accessor");
294
0
    str_val = flb_cfl_ra_translate(rule->ra, NULL, 0, *record_variant, NULL);
295
0
    if (!str_val) {
296
0
        flb_trace("[condition] record accessor translation failed");
297
0
        return FLB_FALSE;
298
0
    }
299
300
0
    flb_trace("[condition] record accessor returned value: '%s'", str_val);
301
302
0
    switch (rule->op) {
303
0
    case FLB_RULE_OP_EQ:
304
0
        flb_trace("[condition] EQ comparison: '%s' == '%s'", str_val, rule->value.str_val);
305
0
        result = (strcmp(str_val, rule->value.str_val) == 0);
306
0
        break;
307
308
0
    case FLB_RULE_OP_NEQ:
309
0
        flb_trace("[condition] NEQ comparison: '%s' != '%s'", str_val, rule->value.str_val);
310
0
        result = (strcmp(str_val, rule->value.str_val) != 0);
311
0
        break;
312
313
0
    case FLB_RULE_OP_GT:
314
0
        num_val = atof(str_val);
315
0
        result = (num_val > rule->value.num_val);
316
0
        break;
317
318
0
    case FLB_RULE_OP_LT:
319
0
        num_val = atof(str_val);
320
0
        result = (num_val < rule->value.num_val);
321
0
        break;
322
323
0
    case FLB_RULE_OP_GTE:
324
0
        num_val = atof(str_val);
325
0
        result = (num_val >= rule->value.num_val);
326
0
        break;
327
328
0
    case FLB_RULE_OP_LTE:
329
0
        num_val = atof(str_val);
330
0
        result = (num_val <= rule->value.num_val);
331
0
        break;
332
333
0
    case FLB_RULE_OP_REGEX:
334
0
        result = (flb_regex_match(rule->regex,
335
0
                                  (unsigned char *)str_val,
336
0
                                  flb_sds_len(str_val)) > 0);
337
0
        break;
338
339
0
    case FLB_RULE_OP_NOT_REGEX:
340
0
        result = (flb_regex_match(rule->regex,
341
0
                                  (unsigned char *)str_val,
342
0
                                  flb_sds_len(str_val)) <= 0);
343
0
        break;
344
345
0
    case FLB_RULE_OP_IN:
346
0
    case FLB_RULE_OP_NOT_IN:
347
0
        for (i = 0; i < rule->value.array.count; i++) {
348
0
            if (strcmp(str_val, rule->value.array.values[i]) == 0) {
349
0
                result = (rule->op == FLB_RULE_OP_IN);
350
0
                break;
351
0
            }
352
0
        }
353
0
        if (i == rule->value.array.count) {
354
0
            result = (rule->op == FLB_RULE_OP_NOT_IN);
355
0
        }
356
0
        break;
357
0
    }
358
359
0
    flb_sds_destroy(str_val);
360
0
    return result;
361
0
}
362
363
int flb_condition_evaluate_ex(struct flb_condition *cond,
364
                              void *ctx,
365
                              flb_condition_get_variant_fn get_variant)
366
0
{
367
0
    struct mk_list *head;
368
0
    struct flb_condition_rule *rule;
369
0
    struct cfl_variant *record_variant;
370
0
    int result;
371
0
    int any_rule_evaluated = FLB_FALSE;
372
0
    int any_rule_matched = FLB_FALSE;
373
374
0
    if (!cond) {
375
0
        flb_trace("[condition] NULL condition, returning TRUE");
376
0
        return FLB_TRUE;
377
0
    }
378
379
0
    if (!get_variant) {
380
0
        flb_trace("[condition] missing variant provider, returning FALSE");
381
0
        return FLB_FALSE;
382
0
    }
383
384
0
    flb_trace("[condition] evaluating condition with %d rules", mk_list_size(&cond->rules));
385
386
0
    if (mk_list_size(&cond->rules) == 0) {
387
0
        flb_trace("[condition] empty rule set, returning default result");
388
0
        return (cond->op == FLB_COND_OP_AND);
389
0
    }
390
391
0
    mk_list_foreach(head, &cond->rules) {
392
0
        rule = mk_list_entry(head, struct flb_condition_rule, _head);
393
0
        flb_trace("[condition] processing rule with op=%d", rule->op);
394
395
0
        record_variant = get_variant(rule, ctx);
396
0
        any_rule_evaluated = FLB_TRUE;
397
0
        if (!record_variant) {
398
0
            flb_trace("[condition] no record variant found for context %d", rule->context);
399
0
            if (cond->op == FLB_COND_OP_AND) {
400
0
                flb_trace("[condition] AND condition missing field, returning FALSE");
401
0
                return FLB_FALSE;
402
0
            }
403
0
            else {
404
0
                continue;
405
0
            }
406
0
        }
407
408
0
        flb_trace("[condition] evaluating rule against record");
409
0
        result = evaluate_rule(rule, record_variant);
410
0
        flb_trace("[condition] rule evaluation result: %d", result);
411
412
0
        if (cond->op == FLB_COND_OP_AND && result == FLB_FALSE) {
413
0
            flb_trace("[condition] AND condition with FALSE result, short-circuiting");
414
0
            return FLB_FALSE;
415
0
        }
416
0
        else if (cond->op == FLB_COND_OP_OR && result == FLB_TRUE) {
417
0
            flb_trace("[condition] OR condition with TRUE result, short-circuiting");
418
0
            return FLB_TRUE;
419
0
        }
420
421
0
        if (result == FLB_TRUE) {
422
0
            any_rule_matched = FLB_TRUE;
423
0
        }
424
0
    }
425
426
0
    if (cond->op == FLB_COND_OP_OR) {
427
0
        flb_trace("[condition] final evaluation result: %d", any_rule_matched);
428
0
        return any_rule_matched;
429
0
    }
430
431
0
    if (any_rule_evaluated == FLB_FALSE) {
432
0
        flb_trace("[condition] no rules evaluated, defaulting to FALSE for AND condition");
433
0
        return FLB_FALSE;
434
0
    }
435
436
0
    flb_trace("[condition] final evaluation result: TRUE");
437
0
    return FLB_TRUE;
438
0
}
439
440
int flb_condition_evaluate(struct flb_condition *cond,
441
                           struct flb_mp_chunk_record *record)
442
0
{
443
0
    if (!cond) {
444
0
        return FLB_TRUE;
445
0
    }
446
447
0
    if (!record) {
448
0
        return FLB_FALSE;
449
0
    }
450
451
0
    return flb_condition_evaluate_ex(cond, record, default_get_record_variant);
452
0
}