| 1 | // SPDX-License-Identifier: GPL-3.0-or-later |
| 2 | |
| 3 | #include "log2journal.h" |
| 4 | |
| 5 | #define PCRE2_ERROR_LINE_MAX 1024 |
| 6 | #define PCRE2_KEY_MAX 1024 |
| 7 | |
| 8 | struct pcre2_state { |
| 9 | LOG_JOB *jb; |
| 10 | |
| 11 | const char *line; |
| 12 | uint32_t pos; |
| 13 | uint32_t key_start; |
| 14 | |
| 15 | pcre2_code *re; |
| 16 | pcre2_match_data *match_data; |
| 17 | |
| 18 | char key[PCRE2_KEY_MAX]; |
| 19 | char msg[PCRE2_ERROR_LINE_MAX]; |
| 20 | }; |
| 21 | |
| 22 | static inline void copy_and_convert_key(PCRE2_STATE *pcre2, const char *key) { |
| 23 | char *d = &pcre2->key[pcre2->key_start]; |
| 24 | size_t remaining = sizeof(pcre2->key) - pcre2->key_start; |
| 25 | |
| 26 | while(remaining >= 2 && *key) { |
| 27 | *d = journal_key_characters_map[(unsigned char)*key]; |
| 28 | remaining--; |
| 29 | key++; |
| 30 | d++; |
| 31 | } |
| 32 | |
| 33 | *d = '\0'; |
| 34 | } |
| 35 | |
| 36 | static inline void jb_traverse_pcre2_named_groups_and_send_keys(PCRE2_STATE *pcre2, pcre2_code *re, pcre2_match_data *match_data, char *line) { |
| 37 | PCRE2_SIZE *ovector = pcre2_get_ovector_pointer(match_data); |
| 38 | uint32_t names_count; |
| 39 | pcre2_pattern_info(re, PCRE2_INFO_NAMECOUNT, &names_count); |
| 40 | |
| 41 | if (names_count > 0) { |
| 42 | PCRE2_SPTR name_table; |
| 43 | pcre2_pattern_info(re, PCRE2_INFO_NAMETABLE, &name_table); |
| 44 | uint32_t name_entry_size; |
| 45 | pcre2_pattern_info(re, PCRE2_INFO_NAMEENTRYSIZE, &name_entry_size); |
| 46 | |
| 47 | const unsigned char *table_ptr = name_table; |
| 48 | for (uint32_t i = 0; i < names_count; i++) { |
| 49 | int n = (table_ptr[0] << 8) | table_ptr[1]; |
| 50 | const char *group_name = (const char *)(table_ptr + 2); |
| 51 | |
| 52 | PCRE2_SIZE start_offset = ovector[2 * n]; |
| 53 | PCRE2_SIZE end_offset = ovector[2 * n + 1]; |
| 54 | PCRE2_SIZE group_length = end_offset - start_offset; |
| 55 | |
| 56 | copy_and_convert_key(pcre2, group_name); |
| 57 | log_job_send_extracted_key_value(pcre2->jb, pcre2->key, line + start_offset, group_length); |
| 58 | |
| 59 | table_ptr += name_entry_size; |
| 60 | } |
| 61 | } |
| 62 | } |
| 63 | |
| 64 | void pcre2_get_error_in_buffer(char *msg, size_t msg_len, int rc, int pos) { |
| 65 | int l; |
| 66 | |
| 67 | if(pos >= 0) |
| 68 | l = snprintf(msg, msg_len, "PCRE2 error %d at pos %d on: ", rc, pos); |
| 69 | else |
| 70 | l = snprintf(msg, msg_len, "PCRE2 error %d on: ", rc); |
| 71 | |
| 72 | pcre2_get_error_message(rc, (PCRE2_UCHAR *)&msg[l], msg_len - l); |
| 73 | } |
| 74 | |
| 75 | static void pcre2_error_message(PCRE2_STATE *pcre2, int rc, int pos) { |
| 76 | pcre2_get_error_in_buffer(pcre2->msg, sizeof(pcre2->msg), rc, pos); |
| 77 | } |
| 78 | |
| 79 | bool pcre2_has_error(PCRE2_STATE *pcre2) { |
| 80 | return !pcre2->re || pcre2->msg[0]; |
| 81 | } |
| 82 | |
| 83 | PCRE2_STATE *pcre2_parser_create(LOG_JOB *jb) { |
| 84 | PCRE2_STATE *pcre2 = mallocz(sizeof(PCRE2_STATE)); |
| 85 | memset(pcre2, 0, sizeof(PCRE2_STATE)); |
| 86 | pcre2->jb = jb; |
| 87 | |
| 88 | if(jb->prefix) |
| 89 | pcre2->key_start = copy_to_buffer(pcre2->key, sizeof(pcre2->key), pcre2->jb->prefix, strlen(pcre2->jb->prefix)); |
| 90 | |
| 91 | int rc; |
| 92 | PCRE2_SIZE pos; |
| 93 | pcre2->re = pcre2_compile((PCRE2_SPTR)jb->pattern, PCRE2_ZERO_TERMINATED, 0, &rc, &pos, NULL); |
| 94 | if (!pcre2->re) { |
| 95 | pcre2_error_message(pcre2, rc, pos); |
| 96 | return pcre2; |
| 97 | } |
| 98 | |
| 99 | pcre2->match_data = pcre2_match_data_create_from_pattern(pcre2->re, NULL); |
| 100 | |
| 101 | return pcre2; |
| 102 | } |
| 103 | |
| 104 | void pcre2_parser_destroy(PCRE2_STATE *pcre2) { |
| 105 | if(pcre2) { |
| 106 | if(pcre2->re) |
| 107 | pcre2_code_free(pcre2->re); |
| 108 | |
| 109 | if(pcre2->match_data) |
| 110 | pcre2_match_data_free(pcre2->match_data); |
| 111 | |
| 112 | freez(pcre2); |
| 113 | } |
| 114 | } |
| 115 | |
| 116 | const char *pcre2_parser_error(PCRE2_STATE *pcre2) { |
| 117 | return pcre2->msg; |
| 118 | } |
| 119 | |
| 120 | bool pcre2_parse_document(PCRE2_STATE *pcre2, const char *txt, size_t len) { |
| 121 | pcre2->line = txt; |
| 122 | pcre2->pos = 0; |
| 123 | pcre2->msg[0] = '\0'; |
| 124 | |
| 125 | if(!len) |
| 126 | len = strlen(txt); |
| 127 | |
| 128 | int rc = pcre2_match(pcre2->re, (PCRE2_SPTR)pcre2->line, len, 0, 0, pcre2->match_data, NULL); |
| 129 | if(rc < 0) { |
| 130 | pcre2_error_message(pcre2, rc, -1); |
| 131 | return false; |
| 132 | } |
| 133 | |
| 134 | jb_traverse_pcre2_named_groups_and_send_keys(pcre2, pcre2->re, pcre2->match_data, (char *)pcre2->line); |
| 135 | |
| 136 | return true; |
| 137 | } |
| 138 | |
| 139 | void pcre2_test(void) { |
| 140 | LOG_JOB jb = { 0 }; |
| 141 | log_job_init(&jb); |
| 142 | log_job_key_prefix_set(&jb, "NGINX_", 6); |
| 143 | PCRE2_STATE *pcre2 = pcre2_parser_create(&jb); |
| 144 | |
| 145 | pcre2_parse_document(pcre2, "{\"value\":\"\\u\\u039A\\u03B1\\u03BB\\u03B7\\u03BC\\u03AD\\u03C1\\u03B1\"}", 0); |
| 146 | |
| 147 | pcre2_parser_destroy(pcre2); |
| 148 | log_job_cleanup(&jb); |
| 149 | } |