| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | // config_yaml.c | ||
| 2 | #include <stdio.h> | ||
| 3 | #include <stdlib.h> | ||
| 4 | #include <string.h> | ||
| 5 | #include <yaml.h> | ||
| 6 | #include "log.h" | ||
| 7 | #include "config.h" | ||
| 8 | |||
| 9 | /* Helper function to find a scalar node within a mapping node given a key */ | ||
| 10 | 21 | static yaml_node_t *find_yaml_node(yaml_document_t *doc, yaml_node_t *node, const char *key) { | |
| 11 |
2/4✓ Branch 0 taken 21 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 21 times.
|
21 | if (!node || node->type != YAML_MAPPING_NODE) |
| 12 | ✗ | return NULL; | |
| 13 | |||
| 14 | 21 | for (yaml_node_pair_t *pair = node->data.mapping.pairs.start; | |
| 15 |
2/2✓ Branch 0 taken 45 times.
✓ Branch 1 taken 4 times.
|
49 | pair < node->data.mapping.pairs.top; pair++) { |
| 16 | 45 | yaml_node_t *key_node = yaml_document_get_node(doc, pair->key); | |
| 17 |
2/4✓ Branch 0 taken 45 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 45 times.
✗ Branch 3 not taken.
|
45 | if (key_node && key_node->type == YAML_SCALAR_NODE && |
| 18 |
2/2✓ Branch 0 taken 17 times.
✓ Branch 1 taken 28 times.
|
45 | strcmp((char *)key_node->data.scalar.value, key) == 0) { |
| 19 | 17 | return yaml_document_get_node(doc, pair->value); | |
| 20 | } | ||
| 21 | } | ||
| 22 | 4 | return NULL; | |
| 23 | } | ||
| 24 | |||
| 25 | /* Helper function to read an integer from a YAML scalar node */ | ||
| 26 | 2 | static int get_yaml_int(yaml_document_t *doc, yaml_node_t *node, int *result) { | |
| 27 | (void)doc; // currently, doc is not directly used here | ||
| 28 |
2/4✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
|
2 | if (node && node->type == YAML_SCALAR_NODE) { |
| 29 | 2 | *result = atoi((char *)node->data.scalar.value); | |
| 30 | 2 | return 0; | |
| 31 | } else { | ||
| 32 | ✗ | if (node) { | |
| 33 | ✗ | log_message(LOG_LEVEL_ERROR, "Parsing error: expected scalar for integer but got type %d at line %d, column %d", | |
| 34 | ✗ | node->type, node->start_mark.line, node->start_mark.column); | |
| 35 | } else { | ||
| 36 | ✗ | log_message(LOG_LEVEL_ERROR, "Parsing error: received NULL node when expecting a scalar for integer"); | |
| 37 | } | ||
| 38 | ✗ | return -1; | |
| 39 | } | ||
| 40 | } | ||
| 41 | |||
| 42 | /* Helper function to read a string from a YAML scalar node */ | ||
| 43 | 12 | static int get_yaml_string(yaml_document_t *doc, yaml_node_t *node, char *buffer, size_t size) { | |
| 44 | (void)doc; // currently, doc is not used here | ||
| 45 |
2/4✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 12 times.
✗ Branch 3 not taken.
|
12 | if (node && node->type == YAML_SCALAR_NODE) { |
| 46 | 12 | strncpy(buffer, (char *)node->data.scalar.value, size - 1); | |
| 47 | 12 | buffer[size - 1] = '\0'; | |
| 48 | 12 | return 0; | |
| 49 | } else { | ||
| 50 | ✗ | if (node) { | |
| 51 | ✗ | log_message(LOG_LEVEL_ERROR, "Parsing error: expected scalar for string but got type %d at line %d, column %d", | |
| 52 | ✗ | node->type, node->start_mark.line, node->start_mark.column); | |
| 53 | } else { | ||
| 54 | ✗ | log_message(LOG_LEVEL_ERROR, "Parsing error: received NULL node when expecting a scalar for string"); | |
| 55 | } | ||
| 56 | ✗ | return -1; | |
| 57 | } | ||
| 58 | } | ||
| 59 | |||
| 60 | /* Parse the logging section for the appender_flags array */ | ||
| 61 | ✗ | static void parse_appender_flags(yaml_document_t *doc, yaml_node_t *node, LoggingConfig *log_cfg) { | |
| 62 | (void)doc; // doc is not needed beyond node extraction here | ||
| 63 | ✗ | if (!node || node->type != YAML_SEQUENCE_NODE) | |
| 64 | ✗ | return; | |
| 65 | |||
| 66 | ✗ | for (yaml_node_item_t *item = node->data.sequence.items.start; | |
| 67 | ✗ | item < node->data.sequence.items.top; item++) { | |
| 68 | ✗ | yaml_node_t *flag_node = yaml_document_get_node(doc, *item); | |
| 69 | ✗ | if (flag_node && flag_node->type == YAML_SCALAR_NODE) { | |
| 70 | ✗ | const char *flag = (char *)flag_node->data.scalar.value; | |
| 71 | ✗ | if (strcmp(flag, "file") == 0) | |
| 72 | ✗ | log_cfg->appender_flags |= APPENDER_FILE; | |
| 73 | ✗ | else if (strcmp(flag, "console") == 0) | |
| 74 | ✗ | log_cfg->appender_flags |= APPENDER_CONSOLE; | |
| 75 | } | ||
| 76 | } | ||
| 77 | } | ||
| 78 | |||
| 79 | /* Function to load the configuration from a YAML file */ | ||
| 80 | 1 | int load_config(ServerConfig *config, const char *file_path) { | |
| 81 | 1 | FILE *fh = fopen(file_path, "rb"); | |
| 82 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | if (!fh) { |
| 83 | ✗ | log_message(LOG_LEVEL_ERROR, "Error opening configuration file: %s", file_path); | |
| 84 | ✗ | return -1; | |
| 85 | } | ||
| 86 | |||
| 87 | yaml_parser_t parser; | ||
| 88 | yaml_document_t document; | ||
| 89 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
|
1 | if (!yaml_parser_initialize(&parser)) { |
| 90 | ✗ | fclose(fh); | |
| 91 | ✗ | log_message(LOG_LEVEL_ERROR, "Unable to initialize YAML parser"); | |
| 92 | ✗ | return -1; | |
| 93 | } | ||
| 94 | 1 | yaml_parser_set_input_file(&parser, fh); | |
| 95 | |||
| 96 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
|
1 | if (!yaml_parser_load(&parser, &document)) { |
| 97 | ✗ | log_message(LOG_LEVEL_ERROR, "Error parsing YAML configuration file"); | |
| 98 | ✗ | yaml_parser_delete(&parser); | |
| 99 | ✗ | fclose(fh); | |
| 100 | ✗ | return -1; | |
| 101 | } | ||
| 102 | 1 | fclose(fh); | |
| 103 | |||
| 104 | 1 | yaml_node_t *root = yaml_document_get_root_node(&document); | |
| 105 |
2/4✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
|
1 | if (!root || root->type != YAML_MAPPING_NODE) { |
| 106 | ✗ | log_message(LOG_LEVEL_ERROR, "YAML document is not a mapping"); | |
| 107 | ✗ | yaml_document_delete(&document); | |
| 108 | ✗ | yaml_parser_delete(&parser); | |
| 109 | ✗ | return -1; | |
| 110 | } | ||
| 111 | |||
| 112 | 1 | memset(config, 0, sizeof(ServerConfig)); | |
| 113 | |||
| 114 | /* Parse global settings under the "server" key */ | ||
| 115 | 1 | yaml_node_t *server_node = find_yaml_node(&document, root, "server"); | |
| 116 |
2/4✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
|
1 | if (server_node && server_node->type == YAML_MAPPING_NODE) { |
| 117 | 1 | yaml_node_t *port_node = find_yaml_node(&document, server_node, "port"); | |
| 118 |
1/2✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
|
1 | if (port_node) |
| 119 | 1 | get_yaml_int(&document, port_node, &config->port); | |
| 120 | |||
| 121 | 1 | yaml_node_t *max_conn_node = find_yaml_node(&document, server_node, "max_connections"); | |
| 122 |
1/2✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
|
1 | if (max_conn_node) |
| 123 | 1 | get_yaml_int(&document, max_conn_node, &config->max_connections); | |
| 124 | |||
| 125 | 1 | yaml_node_t *log_level_node = find_yaml_node(&document, server_node, "log_level"); | |
| 126 |
1/2✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
|
1 | if (log_level_node) |
| 127 | 1 | get_yaml_string(&document, log_level_node, config->log_level, sizeof(config->log_level)); | |
| 128 | } | ||
| 129 | |||
| 130 | /* Parse the logging section */ | ||
| 131 | 1 | yaml_node_t *logging_node = find_yaml_node(&document, root, "logging"); | |
| 132 |
1/4✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
|
1 | if (logging_node && logging_node->type == YAML_MAPPING_NODE) { |
| 133 | ✗ | yaml_node_t *file_node = find_yaml_node(&document, logging_node, "file"); | |
| 134 | ✗ | if (file_node) | |
| 135 | ✗ | get_yaml_string(&document, file_node, config->logging.file, sizeof(config->logging.file)); | |
| 136 | |||
| 137 | ✗ | yaml_node_t *level_node = find_yaml_node(&document, logging_node, "level"); | |
| 138 | ✗ | if (level_node) { | |
| 139 | ✗ | char level_str[16] = {0}; | |
| 140 | ✗ | get_yaml_string(&document, level_node, level_str, sizeof(level_str)); | |
| 141 | ✗ | if (strcmp(level_str, "debug") == 0) | |
| 142 | ✗ | config->logging.level = LOG_LEVEL_DEBUG; | |
| 143 | ✗ | else if (strcmp(level_str, "info") == 0) | |
| 144 | ✗ | config->logging.level = LOG_LEVEL_INFO; | |
| 145 | ✗ | else if (strcmp(level_str, "warn") == 0) | |
| 146 | ✗ | config->logging.level = LOG_LEVEL_WARN; | |
| 147 | ✗ | else if (strcmp(level_str, "error") == 0) | |
| 148 | ✗ | config->logging.level = LOG_LEVEL_ERROR; | |
| 149 | } | ||
| 150 | |||
| 151 | ✗ | yaml_node_t *format_node = find_yaml_node(&document, logging_node, "format"); | |
| 152 | ✗ | if (format_node) { | |
| 153 | ✗ | char format_str[16] = {0}; | |
| 154 | ✗ | get_yaml_string(&document, format_node, format_str, sizeof(format_str)); | |
| 155 | ✗ | config->logging.format = (strcmp(format_str, "json") == 0) ? LOG_FORMAT_JSON : LOG_FORMAT_PLAIN; | |
| 156 | } | ||
| 157 | |||
| 158 | ✗ | yaml_node_t *buffer_size_node = find_yaml_node(&document, logging_node, "buffer_size"); | |
| 159 | ✗ | if (buffer_size_node) | |
| 160 | ✗ | get_yaml_int(&document, buffer_size_node, (int *)&config->logging.buffer_size); | |
| 161 | |||
| 162 | ✗ | yaml_node_t *rollover_size_node = find_yaml_node(&document, logging_node, "rollover_size"); | |
| 163 | ✗ | if (rollover_size_node) | |
| 164 | ✗ | get_yaml_int(&document, rollover_size_node, (int *)&config->logging.rollover_size); | |
| 165 | |||
| 166 | ✗ | yaml_node_t *rollover_daily_node = find_yaml_node(&document, logging_node, "rollover_daily"); | |
| 167 | ✗ | if (rollover_daily_node) { | |
| 168 | ✗ | char daily_str[8] = {0}; | |
| 169 | ✗ | get_yaml_string(&document, rollover_daily_node, daily_str, sizeof(daily_str)); | |
| 170 | ✗ | config->logging.rollover_daily = (strcmp(daily_str, "true") == 0) ? 1 : 0; | |
| 171 | } | ||
| 172 | |||
| 173 | ✗ | yaml_node_t *appender_flags_node = find_yaml_node(&document, logging_node, "appender_flags"); | |
| 174 | ✗ | if (appender_flags_node) | |
| 175 | ✗ | parse_appender_flags(&document, appender_flags_node, &config->logging); | |
| 176 | } | ||
| 177 | |||
| 178 | /* Parse the SSL section */ | ||
| 179 | 1 | yaml_node_t *ssl_node = find_yaml_node(&document, root, "ssl"); | |
| 180 |
2/4✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
|
1 | if (ssl_node && ssl_node->type == YAML_MAPPING_NODE) { |
| 181 | 1 | yaml_node_t *cert_node = find_yaml_node(&document, ssl_node, "certificate"); | |
| 182 |
1/2✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
|
1 | if (cert_node) |
| 183 | 1 | get_yaml_string(&document, cert_node, config->ssl.certificate, sizeof(config->ssl.certificate)); | |
| 184 | 1 | yaml_node_t *key_node = find_yaml_node(&document, ssl_node, "private_key"); | |
| 185 |
1/2✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
|
1 | if (key_node) |
| 186 | 1 | get_yaml_string(&document, key_node, config->ssl.private_key, sizeof(config->ssl.private_key)); | |
| 187 | } | ||
| 188 | |||
| 189 | /* Parse the routes section */ | ||
| 190 | 1 | yaml_node_t *routes_node = find_yaml_node(&document, root, "routes"); | |
| 191 |
2/4✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
|
1 | if (routes_node && routes_node->type == YAML_SEQUENCE_NODE) { |
| 192 | 1 | for (yaml_node_item_t *item = routes_node->data.sequence.items.start; | |
| 193 |
2/2✓ Branch 0 taken 3 times.
✓ Branch 1 taken 1 times.
|
4 | item < routes_node->data.sequence.items.top; item++) { |
| 194 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
|
3 | if (config->route_count >= MAX_ROUTES) |
| 195 | ✗ | break; | |
| 196 | 3 | yaml_node_t *route_node = yaml_document_get_node(&document, *item); | |
| 197 |
2/4✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
|
3 | if (route_node && route_node->type == YAML_MAPPING_NODE) { |
| 198 | 3 | yaml_node_t *path_node = find_yaml_node(&document, route_node, "path"); | |
| 199 |
1/2✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
|
3 | if (path_node) |
| 200 | 3 | get_yaml_string(&document, path_node, | |
| 201 | 3 | config->routes[config->route_count].path, | |
| 202 | sizeof(config->routes[config->route_count].path)); | ||
| 203 | 3 | yaml_node_t *tech_node = find_yaml_node(&document, route_node, "technology"); | |
| 204 |
1/2✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
|
3 | if (tech_node) |
| 205 | 3 | get_yaml_string(&document, tech_node, | |
| 206 | 3 | config->routes[config->route_count].technology, | |
| 207 | sizeof(config->routes[config->route_count].technology)); | ||
| 208 | 3 | yaml_node_t *docroot_node = find_yaml_node(&document, route_node, "document_root"); | |
| 209 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 2 times.
|
3 | if (docroot_node) |
| 210 | 1 | get_yaml_string(&document, docroot_node, | |
| 211 | 1 | config->routes[config->route_count].document_root, | |
| 212 | sizeof(config->routes[config->route_count].document_root)); | ||
| 213 | 3 | yaml_node_t *backend_node = find_yaml_node(&document, route_node, "backend"); | |
| 214 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1 times.
|
3 | if (backend_node) |
| 215 | 2 | get_yaml_string(&document, backend_node, | |
| 216 | 2 | config->routes[config->route_count].backend, | |
| 217 | sizeof(config->routes[config->route_count].backend)); | ||
| 218 | 3 | config->route_count++; | |
| 219 | } | ||
| 220 | } | ||
| 221 | } | ||
| 222 | |||
| 223 | 1 | yaml_document_delete(&document); | |
| 224 | 1 | yaml_parser_delete(&parser); | |
| 225 | 1 | return 0; | |
| 226 | } | ||
| 227 |