master
c 578 lines 21.1 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 #include "log2journal.h"
4
5 // ----------------------------------------------------------------------------
6
7 const char journal_key_characters_map[256] = {
8 // control characters
9 [0] = '\0', [1] = '_', [2] = '_', [3] = '_', [4] = '_', [5] = '_', [6] = '_', [7] = '_',
10 [8] = '_', [9] = '_', [10] = '_', [11] = '_', [12] = '_', [13] = '_', [14] = '_', [15] = '_',
11 [16] = '_', [17] = '_', [18] = '_', [19] = '_', [20] = '_', [21] = '_', [22] = '_', [23] = '_',
12 [24] = '_', [25] = '_', [26] = '_', [27] = '_', [28] = '_', [29] = '_', [30] = '_', [31] = '_',
13
14 // symbols
15 [' '] = '_', ['!'] = '_', ['"'] = '_', ['#'] = '_', ['$'] = '_', ['%'] = '_', ['&'] = '_', ['\''] = '_',
16 ['('] = '_', [')'] = '_', ['*'] = '_', ['+'] = '_', [','] = '_', ['-'] = '_', ['.'] = '_', ['/'] = '_',
17
18 // numbers
19 ['0'] = '0', ['1'] = '1', ['2'] = '2', ['3'] = '3', ['4'] = '4', ['5'] = '5', ['6'] = '6', ['7'] = '7',
20 ['8'] = '8', ['9'] = '9',
21
22 // symbols
23 [':'] = '_', [';'] = '_', ['<'] = '_', ['='] = '_', ['>'] = '_', ['?'] = '_', ['@'] = '_',
24
25 // capitals
26 ['A'] = 'A', ['B'] = 'B', ['C'] = 'C', ['D'] = 'D', ['E'] = 'E', ['F'] = 'F', ['G'] = 'G', ['H'] = 'H',
27 ['I'] = 'I', ['J'] = 'J', ['K'] = 'K', ['L'] = 'L', ['M'] = 'M', ['N'] = 'N', ['O'] = 'O', ['P'] = 'P',
28 ['Q'] = 'Q', ['R'] = 'R', ['S'] = 'S', ['T'] = 'T', ['U'] = 'U', ['V'] = 'V', ['W'] = 'W', ['X'] = 'X',
29 ['Y'] = 'Y', ['Z'] = 'Z',
30
31 // symbols
32 ['['] = '_', ['\\'] = '_', [']'] = '_', ['^'] = '_', ['_'] = '_', ['`'] = '_',
33
34 // lower to upper
35 ['a'] = 'A', ['b'] = 'B', ['c'] = 'C', ['d'] = 'D', ['e'] = 'E', ['f'] = 'F', ['g'] = 'G', ['h'] = 'H',
36 ['i'] = 'I', ['j'] = 'J', ['k'] = 'K', ['l'] = 'L', ['m'] = 'M', ['n'] = 'N', ['o'] = 'O', ['p'] = 'P',
37 ['q'] = 'Q', ['r'] = 'R', ['s'] = 'S', ['t'] = 'T', ['u'] = 'U', ['v'] = 'V', ['w'] = 'W', ['x'] = 'X',
38 ['y'] = 'Y', ['z'] = 'Z',
39
40 // symbols
41 ['{'] = '_', ['|'] = '_', ['}'] = '_', ['~'] = '_', [127] = '_', // Delete (DEL)
42
43 // Extended ASCII characters (128-255) set to underscore
44 [128] = '_', [129] = '_', [130] = '_', [131] = '_', [132] = '_', [133] = '_', [134] = '_', [135] = '_',
45 [136] = '_', [137] = '_', [138] = '_', [139] = '_', [140] = '_', [141] = '_', [142] = '_', [143] = '_',
46 [144] = '_', [145] = '_', [146] = '_', [147] = '_', [148] = '_', [149] = '_', [150] = '_', [151] = '_',
47 [152] = '_', [153] = '_', [154] = '_', [155] = '_', [156] = '_', [157] = '_', [158] = '_', [159] = '_',
48 [160] = '_', [161] = '_', [162] = '_', [163] = '_', [164] = '_', [165] = '_', [166] = '_', [167] = '_',
49 [168] = '_', [169] = '_', [170] = '_', [171] = '_', [172] = '_', [173] = '_', [174] = '_', [175] = '_',
50 [176] = '_', [177] = '_', [178] = '_', [179] = '_', [180] = '_', [181] = '_', [182] = '_', [183] = '_',
51 [184] = '_', [185] = '_', [186] = '_', [187] = '_', [188] = '_', [189] = '_', [190] = '_', [191] = '_',
52 [192] = '_', [193] = '_', [194] = '_', [195] = '_', [196] = '_', [197] = '_', [198] = '_', [199] = '_',
53 [200] = '_', [201] = '_', [202] = '_', [203] = '_', [204] = '_', [205] = '_', [206] = '_', [207] = '_',
54 [208] = '_', [209] = '_', [210] = '_', [211] = '_', [212] = '_', [213] = '_', [214] = '_', [215] = '_',
55 [216] = '_', [217] = '_', [218] = '_', [219] = '_', [220] = '_', [221] = '_', [222] = '_', [223] = '_',
56 [224] = '_', [225] = '_', [226] = '_', [227] = '_', [228] = '_', [229] = '_', [230] = '_', [231] = '_',
57 [232] = '_', [233] = '_', [234] = '_', [235] = '_', [236] = '_', [237] = '_', [238] = '_', [239] = '_',
58 [240] = '_', [241] = '_', [242] = '_', [243] = '_', [244] = '_', [245] = '_', [246] = '_', [247] = '_',
59 [248] = '_', [249] = '_', [250] = '_', [251] = '_', [252] = '_', [253] = '_', [254] = '_', [255] = '_',
60 };
61
62 // ----------------------------------------------------------------------------
63
64 static inline HASHED_KEY *get_key_from_hashtable(LOG_JOB *jb, HASHED_KEY *k) {
65 if(k->flags & HK_HASHTABLE_ALLOCATED)
66 return k;
67
68 if(!k->hashtable_ptr) {
69 HASHED_KEY *ht_key;
70 SIMPLE_HASHTABLE_SLOT_KEY *slot = simple_hashtable_get_slot_KEY(&jb->hashtable, k->hash, NULL, true);
71 if((ht_key = SIMPLE_HASHTABLE_SLOT_DATA(slot))) {
72 if(!(ht_key->flags & HK_COLLISION_CHECKED)) {
73 ht_key->flags |= HK_COLLISION_CHECKED;
74
75 if(strcmp(ht_key->key, k->key) != 0)
76 l2j_log(
77 "Hashtable collision detected on key '%s' (hash %lx) and '%s' (hash %lx). "
78 "Please file a bug report.",
79 ht_key->key,
80 (unsigned long)ht_key->hash,
81 k->key,
82 (unsigned long)k->hash);
83 }
84 }
85 else {
86 ht_key = callocz(1, sizeof(HASHED_KEY));
87 ht_key->key = strdupz(k->key);
88 ht_key->len = k->len;
89 ht_key->hash = k->hash;
90 ht_key->flags = HK_HASHTABLE_ALLOCATED;
91
92 simple_hashtable_set_slot_KEY(&jb->hashtable, slot, ht_key->hash, ht_key);
93 }
94
95 k->hashtable_ptr = ht_key;
96 }
97
98 return k->hashtable_ptr;
99 }
100
101 static inline HASHED_KEY *get_key_from_hashtable_with_char_ptr(LOG_JOB *jb, const char *key) {
102 HASHED_KEY find = {
103 .flags = HK_NONE,
104 .key = key,
105 .len = strlen(key),
106 };
107 find.hash = XXH3_64bits(key, find.len);
108
109 return get_key_from_hashtable(jb, &find);
110 }
111
112 // ----------------------------------------------------------------------------
113
114 static inline void validate_key(LOG_JOB *jb __maybe_unused, HASHED_KEY *k) {
115 if(k->len > JOURNAL_MAX_KEY_LEN)
116 l2j_log(
117 "WARNING: key '%s' has length %zu, which is more than %zu, the max systemd-journal allows",
118 k->key,
119 (size_t)k->len,
120 (size_t)JOURNAL_MAX_KEY_LEN);
121
122 for(size_t i = 0; i < k->len ;i++) {
123 char c = k->key[i];
124
125 if((c < 'A' || c > 'Z') && !isdigit(c) && c != '_') {
126 l2j_log("WARNING: key '%s' contains characters that are not allowed by systemd-journal.", k->key);
127 break;
128 }
129 }
130
131 if(isdigit(k->key[0]))
132 l2j_log("WARNING: key '%s' starts with a digit and may not be accepted by systemd-journal.", k->key);
133
134 if(k->key[0] == '_')
135 l2j_log(
136 "WARNING: key '%s' starts with an underscore, which makes it a systemd-journal trusted field. "
137 "Such fields are accepted by systemd-journal-remote, but not by systemd-journald.",
138 k->key);
139 }
140
141 // ----------------------------------------------------------------------------
142
143 static inline size_t replace_evaluate_to_buffer(LOG_JOB *jb, HASHED_KEY *k __maybe_unused, REPLACE_PATTERN *rp, char *dst, size_t dst_size) {
144 size_t remaining = dst_size;
145 char *copy_to = dst;
146
147 for(REPLACE_NODE *node = rp->nodes; node != NULL && remaining > 1; node = node->next) {
148 if(node->is_variable) {
149 if(hashed_keys_match(&node->name, &jb->line.key)) {
150 size_t copied = copy_to_buffer(copy_to, remaining, jb->line.trimmed, jb->line.trimmed_len);
151 copy_to += copied;
152 remaining -= copied;
153 }
154 else {
155 HASHED_KEY *ktmp = get_key_from_hashtable_with_char_ptr(jb, node->name.key);
156 if(ktmp->value.len) {
157 size_t copied = copy_to_buffer(copy_to, remaining, ktmp->value.txt, ktmp->value.len);
158 copy_to += copied;
159 remaining -= copied;
160 }
161 }
162 }
163 else {
164 size_t copied = copy_to_buffer(copy_to, remaining, node->name.key, node->name.len);
165 copy_to += copied;
166 remaining -= copied;
167 }
168 }
169
170 return copy_to - dst;
171 }
172
173 static inline void replace_evaluate(LOG_JOB *jb, HASHED_KEY *k, REPLACE_PATTERN *rp) {
174 HASHED_KEY *ht_key = get_key_from_hashtable(jb, k);
175
176 // set it to empty value
177 k->value.len = 0;
178
179 for(REPLACE_NODE *node = rp->nodes; node != NULL; node = node->next) {
180 if(node->is_variable) {
181 if(hashed_keys_match(&node->name, &jb->line.key))
182 txt_l2j_append(&ht_key->value, jb->line.trimmed, jb->line.trimmed_len);
183
184 else {
185 HASHED_KEY *ktmp = get_key_from_hashtable_with_char_ptr(jb, node->name.key);
186 if(ktmp->value.len)
187 txt_l2j_append(&ht_key->value, ktmp->value.txt, ktmp->value.len);
188 }
189 }
190 else
191 txt_l2j_append(&ht_key->value, node->name.key, node->name.len);
192 }
193 }
194
195 static inline void replace_evaluate_from_pcre2(LOG_JOB *jb, HASHED_KEY *k, REPLACE_PATTERN *rp, SEARCH_PATTERN *sp) {
196 assert(k->flags & HK_HASHTABLE_ALLOCATED);
197
198 // set the temporary TEXT to zero length
199 jb->rewrites.tmp.len = 0;
200
201 PCRE2_SIZE *ovector = pcre2_get_ovector_pointer(sp->match_data);
202
203 // Iterate through the linked list of replacement nodes
204 for(REPLACE_NODE *node = rp->nodes; node != NULL; node = node->next) {
205 if(node->is_variable) {
206 int group_number = pcre2_substring_number_from_name(
207 sp->re, (PCRE2_SPTR) node->name.key);
208
209 if(group_number >= 0) {
210 PCRE2_SIZE start_offset = ovector[2 * group_number];
211 PCRE2_SIZE end_offset = ovector[2 * group_number + 1];
212 PCRE2_SIZE length = end_offset - start_offset;
213
214 txt_l2j_append(&jb->rewrites.tmp, k->value.txt + start_offset, length);
215 }
216 else {
217 if(hashed_keys_match(&node->name, &jb->line.key))
218 txt_l2j_append(&jb->rewrites.tmp, jb->line.trimmed, jb->line.trimmed_len);
219
220 else {
221 HASHED_KEY *ktmp = get_key_from_hashtable_with_char_ptr(jb, node->name.key);
222 if(ktmp->value.len)
223 txt_l2j_append(&jb->rewrites.tmp, ktmp->value.txt, ktmp->value.len);
224 }
225 }
226 }
227 else {
228 txt_l2j_append(&jb->rewrites.tmp, node->name.key, node->name.len);
229 }
230 }
231
232 // swap the values of the temporary TEXT and the key value
233 TXT_L2J tmp = k->value;
234 k->value = jb->rewrites.tmp;
235 jb->rewrites.tmp = tmp;
236 }
237
238 static inline bool rewrite_conditions_satisfied(LOG_JOB *jb, HASHED_KEY *k, REWRITE *rw) {
239 assert(k->flags & HK_HASHTABLE_ALLOCATED);
240
241 if(rw->flags & RW_MATCH_PCRE2) {
242 return search_pattern_matches(&rw->match_pcre2, k->value.txt, k->value.len);
243 }
244 else if(rw->flags & RW_MATCH_NON_EMPTY) {
245 char buffer[2]; // we don't need a big buffer - we just check if anything is written
246 if(replace_evaluate_to_buffer(jb, k, &rw->match_non_empty, buffer, sizeof(buffer)))
247 // it copied something
248 return true;
249 else
250 // it copied nothing
251 return false;
252 }
253 else
254 // no conditions
255 return true;
256 }
257
258 // ----------------------------------------------------------------------------
259
260 static inline HASHED_KEY *rename_key(LOG_JOB *jb, HASHED_KEY *k) {
261 if(!(k->flags & HK_RENAMES_CHECKED) || k->flags & HK_HAS_RENAMES) {
262 k->flags |= HK_RENAMES_CHECKED;
263
264 for(size_t i = 0; i < jb->renames.used; i++) {
265 RENAME *rn = &jb->renames.array[i];
266
267 if(hashed_keys_match(&rn->old_key, k)) {
268 k->flags |= HK_HAS_RENAMES;
269
270 return get_key_from_hashtable(jb, &rn->new_key);
271 }
272 }
273 }
274
275 return k;
276 }
277
278 // ----------------------------------------------------------------------------
279
280 static inline void send_key_value_constant(LOG_JOB *jb __maybe_unused, HASHED_KEY *key, const char *value, size_t len) {
281 HASHED_KEY *ht_key = get_key_from_hashtable(jb, key);
282
283 txt_l2j_set(&ht_key->value, value, len);
284 ht_key->flags |= HK_VALUE_FROM_LOG;
285
286 // fprintf(stderr, "SET %s=%.*s\n", ht_key->key, (int)ht_key->value.len, ht_key->value.txt);
287 }
288
289 static inline void send_key_value_error(LOG_JOB *jb, HASHED_KEY *key, const char *format, ...) PRINTFLIKE(3, 4);
290 static inline void send_key_value_error(LOG_JOB *jb, HASHED_KEY *key, const char *format, ...) {
291 HASHED_KEY *ht_key = get_key_from_hashtable(jb, key);
292
293 printf("%s=", ht_key->key);
294 va_list args;
295 va_start(args, format);
296 vprintf(format, args);
297 va_end(args);
298 printf("\n");
299 }
300
301 inline void log_job_send_extracted_key_value(LOG_JOB *jb, const char *key, const char *value, size_t len) {
302 HASHED_KEY *ht_key = get_key_from_hashtable_with_char_ptr(jb, key);
303 HASHED_KEY *nk = rename_key(jb, ht_key);
304 txt_l2j_set(&nk->value, value, len);
305 ht_key->flags |= HK_VALUE_FROM_LOG;
306
307 // fprintf(stderr, "SET %s=%.*s\n", ht_key->key, (int)ht_key->value.len, ht_key->value.txt);
308 }
309
310 static inline void log_job_process_rewrites(LOG_JOB *jb) {
311 for(size_t i = 0; i < jb->rewrites.used ;i++) {
312 REWRITE *rw = &jb->rewrites.array[i];
313
314 HASHED_KEY *k = get_key_from_hashtable(jb, &rw->key);
315
316 if(!(rw->flags & RW_INJECT) && !(k->flags & HK_VALUE_FROM_LOG) && !k->value.len)
317 continue;
318
319 if(!(k->flags & HK_VALUE_REWRITTEN) && rewrite_conditions_satisfied(jb, k, rw)) {
320 if(rw->flags & RW_MATCH_PCRE2)
321 replace_evaluate_from_pcre2(jb, k, &rw->value, &rw->match_pcre2);
322 else
323 replace_evaluate(jb, k, &rw->value);
324
325 if(!(rw->flags & RW_DONT_STOP))
326 k->flags |= HK_VALUE_REWRITTEN;
327
328 // fprintf(stderr, "REWRITE %s=%.*s\n", k->key, (int)k->value.len, k->value.txt);
329 }
330 }
331 }
332
333 static inline void send_all_fields(LOG_JOB *jb) {
334 SIMPLE_HASHTABLE_SORTED_FOREACH_READ_ONLY(&jb->hashtable, kptr, HASHED_KEY, _KEY) {
335 HASHED_KEY *k = SIMPLE_HASHTABLE_SORTED_FOREACH_READ_ONLY_VALUE(kptr);
336
337 if(k->value.len) {
338 // the key exists and has some value
339
340 if(!(k->flags & HK_FILTERED)) {
341 k->flags |= HK_FILTERED;
342
343 bool included = jb->filter.include.re ? search_pattern_matches(&jb->filter.include, k->key, k->len) : true;
344 bool excluded = jb->filter.exclude.re ? search_pattern_matches(&jb->filter.exclude, k->key, k->len) : false;
345
346 if(included && !excluded)
347 k->flags |= HK_FILTERED_INCLUDED;
348 else
349 k->flags &= ~HK_FILTERED_INCLUDED;
350
351 // log some error if the key does not comply to journal standards
352 validate_key(jb, k);
353 }
354
355 if(k->flags & HK_FILTERED_INCLUDED)
356 printf("%s=%.*s\n", k->key, (int)k->value.len, k->value.txt);
357
358 // reset it for the next round
359 k->value.txt[0] = '\0';
360 k->value.len = 0;
361 }
362
363 k->flags &= ~(HK_VALUE_REWRITTEN | HK_VALUE_FROM_LOG);
364 }
365 }
366
367 // ----------------------------------------------------------------------------
368 // injection of constant fields
369
370 static void select_which_injections_should_be_injected_on_unmatched(LOG_JOB *jb) {
371 // mark all injections to be added to unmatched logs
372 for(size_t i = 0; i < jb->injections.used ; i++)
373 jb->injections.keys[i].on_unmatched = true;
374
375 if(jb->injections.used && jb->unmatched.injections.used) {
376 // we have both injections and injections on unmatched
377
378 // we find all the injections that are also configured as injections on unmatched,
379 // and we disable them, so that the output will not have the same key twice
380
381 for(size_t i = 0; i < jb->injections.used ;i++) {
382 for(size_t u = 0; u < jb->unmatched.injections.used ; u++) {
383 if(strcmp(jb->injections.keys[i].key.key, jb->unmatched.injections.keys[u].key.key) == 0)
384 jb->injections.keys[i].on_unmatched = false;
385 }
386 }
387 }
388 }
389
390
391 static inline void jb_finalize_injections(LOG_JOB *jb, bool line_is_matched) {
392 for (size_t j = 0; j < jb->injections.used; j++) {
393 if(!line_is_matched && !jb->injections.keys[j].on_unmatched)
394 continue;
395
396 INJECTION *inj = &jb->injections.keys[j];
397
398 replace_evaluate(jb, &inj->key, &inj->value);
399 }
400 }
401
402 // ----------------------------------------------------------------------------
403 // filename injection
404
405 static inline void jb_inject_filename(LOG_JOB *jb) {
406 if (jb->filename.key.key && jb->filename.current.len)
407 send_key_value_constant(jb, &jb->filename.key, jb->filename.current.txt, jb->filename.current.len);
408 }
409
410 static inline bool jb_switched_filename(LOG_JOB *jb, const char *line, size_t len) {
411 // IMPORTANT:
412 // Return TRUE when the caller should skip this line (because it is ours).
413 // Unfortunately, we have to consume empty lines too.
414
415 // IMPORTANT:
416 // filename may not be NULL terminated and have more data than the filename.
417
418 if (!len) {
419 jb->filename.last_line_was_empty = true;
420 return true;
421 }
422
423 // Check if it's a log file change line
424 if (jb->filename.last_line_was_empty && line[0] == '=' && strncmp(line, "==> ", 4) == 0) {
425 const char *start = line + 4;
426 const char *end = strstr(line, " <==");
427 while (*start == ' ') start++;
428 if (*start != '\n' && *start != '\0' && end) {
429 txt_l2j_set(&jb->filename.current, start, end - start);
430 return true;
431 }
432 }
433
434 jb->filename.last_line_was_empty = false;
435 return false;
436 }
437
438 static inline bool jb_send_unmatched_line(LOG_JOB *jb, const char *line) {
439 if (!jb->unmatched.key.key)
440 return false;
441
442 // we are sending errors to systemd-journal
443 send_key_value_error(jb, &jb->unmatched.key, "Parsing error on: %s", line);
444
445 for (size_t j = 0; j < jb->unmatched.injections.used; j++) {
446 INJECTION *inj = &jb->unmatched.injections.keys[j];
447
448 replace_evaluate(jb, &inj->key, &inj->value);
449 }
450
451 return true;
452 }
453
454 // ----------------------------------------------------------------------------
455 // running a job
456
457 static char *get_next_line(LOG_JOB *jb __maybe_unused, char *buffer, size_t size, size_t *line_length) {
458 if(!fgets(buffer, (int)size, stdin)) {
459 *line_length = 0;
460 return NULL;
461 }
462
463 char *line = buffer;
464 size_t len = strlen(line);
465
466 // remove trailing newlines and spaces
467 while(len > 1 && (line[len - 1] == '\n' || isspace(line[len - 1])))
468 line[--len] = '\0';
469
470 // skip leading spaces
471 while(isspace(*line)) {
472 line++;
473 len--;
474 }
475
476 *line_length = len;
477 return line;
478 }
479
480 int log_job_run(LOG_JOB *jb) {
481 select_which_injections_should_be_injected_on_unmatched(jb);
482
483 PCRE2_STATE *pcre2 = NULL;
484 LOG_JSON_STATE *json = NULL;
485 LOGFMT_STATE *logfmt = NULL;
486
487 if(strcmp(jb->pattern, "json") == 0) {
488 json = json_parser_create(jb);
489 // never fails
490 }
491 else if(strcmp(jb->pattern, "logfmt") == 0) {
492 logfmt = logfmt_parser_create(jb);
493 // never fails
494 }
495 else if(strcmp(jb->pattern, "none") != 0) {
496 pcre2 = pcre2_parser_create(jb);
497 if(pcre2_has_error(pcre2)) {
498 l2j_log("%s", pcre2_parser_error(pcre2));
499 pcre2_parser_destroy(pcre2);
500 return 1;
501 }
502 }
503
504 jb->line.buffer = mallocz(MAX_LINE_LENGTH + 1);
505 jb->line.size = MAX_LINE_LENGTH + 1;
506 jb->line.trimmed_len = 0;
507 jb->line.trimmed = jb->line.buffer;
508
509 while ((jb->line.trimmed = get_next_line(jb, (char *)jb->line.buffer, jb->line.size, &jb->line.trimmed_len))) {
510 const char *line = jb->line.trimmed;
511 size_t len = jb->line.trimmed_len;
512
513 if(jb_switched_filename(jb, line, len))
514 continue;
515
516 bool line_is_matched = true;
517
518 if(json)
519 line_is_matched = json_parse_document(json, line);
520 else if(logfmt)
521 line_is_matched = logfmt_parse_document(logfmt, line);
522 else if(pcre2)
523 line_is_matched = pcre2_parse_document(pcre2, line, len);
524
525 if(!line_is_matched) {
526 if(json)
527 l2j_log("%s", json_parser_error(json));
528 else if(logfmt)
529 l2j_log("%s", logfmt_parser_error(logfmt));
530 else if(pcre2)
531 l2j_log("%s", pcre2_parser_error(pcre2));
532
533 if(!jb_send_unmatched_line(jb, line))
534 // just logging to stderr, not sending unmatched lines
535 continue;
536 }
537
538 jb_inject_filename(jb);
539 jb_finalize_injections(jb, line_is_matched);
540
541 log_job_process_rewrites(jb);
542 send_all_fields(jb);
543 printf("\n");
544 fflush(stdout);
545 }
546
547 if(json)
548 json_parser_destroy(json);
549
550 else if(logfmt)
551 logfmt_parser_destroy(logfmt);
552
553 else if(pcre2)
554 pcre2_parser_destroy(pcre2);
555
556 freez((void *)jb->line.buffer);
557
558 return 0;
559 }
560
561 // ----------------------------------------------------------------------------
562
563 int main(int argc, char *argv[]) {
564 LOG_JOB log_job;
565
566 log_job_init(&log_job);
567
568 if(!log_job_command_line_parse_parameters(&log_job, argc, argv))
569 exit(1);
570
571 if(log_job.show_config)
572 log_job_configuration_to_yaml(&log_job);
573
574 int ret = log_job_run(&log_job);
575
576 log_job_cleanup(&log_job);
577 return ret;
578 }