| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | #include <stdio.h> | ||
| 2 | #include <stdlib.h> | ||
| 3 | #include <string.h> | ||
| 4 | #include <strings.h> | ||
| 5 | #include <errno.h> | ||
| 6 | #include <limits.h> | ||
| 7 | #include <yaml.h> | ||
| 8 | #include "log.h" | ||
| 9 | #include "config.h" | ||
| 10 | |||
| 11 | 240 | static yaml_node_t *find_yaml_node(yaml_document_t *doc, yaml_node_t *node, const char *key) | |
| 12 | { | ||
| 13 |
2/4✓ Branch 0 taken 240 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 240 times.
|
240 | if (!node || node->type != YAML_MAPPING_NODE) |
| 14 | ✗ | return NULL; | |
| 15 | |||
| 16 | 240 | for (yaml_node_pair_t *pair = node->data.mapping.pairs.start; | |
| 17 |
2/2✓ Branch 0 taken 624 times.
✓ Branch 1 taken 26 times.
|
650 | pair < node->data.mapping.pairs.top; pair++) { |
| 18 | 624 | yaml_node_t *key_node = yaml_document_get_node(doc, pair->key); | |
| 19 |
2/4✓ Branch 0 taken 624 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 624 times.
✗ Branch 3 not taken.
|
624 | if (key_node && key_node->type == YAML_SCALAR_NODE && |
| 20 |
2/2✓ Branch 0 taken 214 times.
✓ Branch 1 taken 410 times.
|
624 | strcmp((char *)key_node->data.scalar.value, key) == 0) { |
| 21 | 214 | return yaml_document_get_node(doc, pair->value); | |
| 22 | } | ||
| 23 | } | ||
| 24 | 26 | return NULL; | |
| 25 | } | ||
| 26 | |||
| 27 | 13 | static void set_config_defaults(ServerConfig *config) | |
| 28 | { | ||
| 29 | 13 | memset(config, 0, sizeof(*config)); | |
| 30 | 13 | config->port = 8443; | |
| 31 | 13 | config->max_connections = 100; | |
| 32 | 13 | snprintf(config->log_level, sizeof(config->log_level), "info"); | |
| 33 | |||
| 34 | 13 | config->logging.level = LOG_LEVEL_DEBUG; | |
| 35 | 13 | config->logging.format = LOG_FORMAT_PLAIN; | |
| 36 | 13 | config->logging.buffer_size = 16384; | |
| 37 | 13 | config->logging.rollover_size = 10485760; | |
| 38 | 13 | config->logging.rollover_daily = 1; | |
| 39 | 13 | config->logging.appender_flags = APPENDER_FILE | APPENDER_CONSOLE; | |
| 40 | 13 | snprintf(config->logging.file, sizeof(config->logging.file), "emme.log"); | |
| 41 | |||
| 42 | 13 | snprintf(config->ssl.certificate, sizeof(config->ssl.certificate), "certs/dev.crt"); | |
| 43 | 13 | snprintf(config->ssl.private_key, sizeof(config->ssl.private_key), "certs/dev.key"); | |
| 44 | 13 | } | |
| 45 | |||
| 46 | 116 | static int get_yaml_string(yaml_node_t *node, const char *field, char *buffer, size_t size) | |
| 47 | { | ||
| 48 | int written; | ||
| 49 | const char *value; | ||
| 50 | |||
| 51 |
2/4✓ Branch 0 taken 116 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 116 times.
|
116 | if (!node || node->type != YAML_SCALAR_NODE) { |
| 52 | ✗ | fprintf(stderr, "Invalid '%s': expected a scalar string\n", field); | |
| 53 | ✗ | return -1; | |
| 54 | } | ||
| 55 | |||
| 56 | 116 | value = (const char *)node->data.scalar.value; | |
| 57 | 116 | written = snprintf(buffer, size, "%s", value); | |
| 58 |
2/4✓ Branch 0 taken 116 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 116 times.
|
116 | if (written < 0 || (size_t)written >= size) { |
| 59 | ✗ | fprintf(stderr, "Invalid '%s': value too long\n", field); | |
| 60 | ✗ | return -1; | |
| 61 | } | ||
| 62 | 116 | return 0; | |
| 63 | } | ||
| 64 | |||
| 65 | 19 | static int parse_int_in_range(const char *text, int min, int max, int *result) | |
| 66 | { | ||
| 67 | 19 | char *end = NULL; | |
| 68 | long value; | ||
| 69 | |||
| 70 | 19 | errno = 0; | |
| 71 | 19 | value = strtol(text, &end, 10); | |
| 72 |
4/6✓ Branch 0 taken 19 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 18 times.
|
19 | if (errno != 0 || end == text || *end != '\0') |
| 73 | 1 | return -1; | |
| 74 |
2/4✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 18 times.
|
18 | if (value < min || value > max) |
| 75 | ✗ | return -1; | |
| 76 | |||
| 77 | 18 | *result = (int)value; | |
| 78 | 18 | return 0; | |
| 79 | } | ||
| 80 | |||
| 81 | 18 | static int parse_size_in_range(const char *text, size_t min, size_t max, size_t *result) | |
| 82 | { | ||
| 83 | 18 | char *end = NULL; | |
| 84 | unsigned long long value; | ||
| 85 | |||
| 86 | 18 | errno = 0; | |
| 87 | 18 | value = strtoull(text, &end, 10); | |
| 88 |
3/6✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 18 times.
|
18 | if (errno != 0 || end == text || *end != '\0') |
| 89 | ✗ | return -1; | |
| 90 |
2/4✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 18 times.
|
18 | if (value < min || value > max) |
| 91 | ✗ | return -1; | |
| 92 | |||
| 93 | 18 | *result = (size_t)value; | |
| 94 | 18 | return 0; | |
| 95 | } | ||
| 96 | |||
| 97 | 19 | static int get_yaml_int_in_range(yaml_node_t *node, const char *field, | |
| 98 | int min, int max, int *result) | ||
| 99 | { | ||
| 100 |
2/4✓ Branch 0 taken 19 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 19 times.
|
19 | if (!node || node->type != YAML_SCALAR_NODE) { |
| 101 | ✗ | fprintf(stderr, "Invalid '%s': expected integer scalar\n", field); | |
| 102 | ✗ | return -1; | |
| 103 | } | ||
| 104 |
2/2✓ Branch 1 taken 1 times.
✓ Branch 2 taken 18 times.
|
19 | if (parse_int_in_range((const char *)node->data.scalar.value, min, max, result) != 0) { |
| 105 | 1 | fprintf(stderr, "Invalid '%s': expected integer in range [%d, %d]\n", | |
| 106 | field, min, max); | ||
| 107 | 1 | return -1; | |
| 108 | } | ||
| 109 | 18 | return 0; | |
| 110 | } | ||
| 111 | |||
| 112 | 18 | static int get_yaml_size_in_range(yaml_node_t *node, const char *field, | |
| 113 | size_t min, size_t max, size_t *result) | ||
| 114 | { | ||
| 115 |
2/4✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 18 times.
|
18 | if (!node || node->type != YAML_SCALAR_NODE) { |
| 116 | ✗ | fprintf(stderr, "Invalid '%s': expected integer scalar\n", field); | |
| 117 | ✗ | return -1; | |
| 118 | } | ||
| 119 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
|
18 | if (parse_size_in_range((const char *)node->data.scalar.value, min, max, result) != 0) { |
| 120 | ✗ | fprintf(stderr, "Invalid '%s': expected integer in range [%zu, %zu]\n", | |
| 121 | field, min, max); | ||
| 122 | ✗ | return -1; | |
| 123 | } | ||
| 124 | 18 | return 0; | |
| 125 | } | ||
| 126 | |||
| 127 | 9 | static int get_yaml_bool(yaml_node_t *node, const char *field, int *result) | |
| 128 | { | ||
| 129 | const char *value; | ||
| 130 | |||
| 131 |
2/4✓ Branch 0 taken 9 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 9 times.
|
9 | if (!node || node->type != YAML_SCALAR_NODE) { |
| 132 | ✗ | fprintf(stderr, "Invalid '%s': expected boolean scalar\n", field); | |
| 133 | ✗ | return -1; | |
| 134 | } | ||
| 135 | |||
| 136 | 9 | value = (const char *)node->data.scalar.value; | |
| 137 |
1/6✗ Branch 0 not taken.
✓ Branch 1 taken 9 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
|
9 | if (strcasecmp(value, "true") == 0 || strcmp(value, "1") == 0 || strcasecmp(value, "yes") == 0) { |
| 138 | 9 | *result = 1; | |
| 139 | 9 | return 0; | |
| 140 | } | ||
| 141 | ✗ | if (strcasecmp(value, "false") == 0 || strcmp(value, "0") == 0 || strcasecmp(value, "no") == 0) { | |
| 142 | ✗ | *result = 0; | |
| 143 | ✗ | return 0; | |
| 144 | } | ||
| 145 | |||
| 146 | ✗ | fprintf(stderr, "Invalid '%s': expected true/false\n", field); | |
| 147 | ✗ | return -1; | |
| 148 | } | ||
| 149 | |||
| 150 | 9 | static int parse_log_level(const char *value, LogLevel *level) | |
| 151 | { | ||
| 152 |
1/2✓ Branch 0 taken 9 times.
✗ Branch 1 not taken.
|
9 | if (strcasecmp(value, "debug") == 0) |
| 153 | 9 | *level = LOG_LEVEL_DEBUG; | |
| 154 | ✗ | else if (strcasecmp(value, "info") == 0) | |
| 155 | ✗ | *level = LOG_LEVEL_INFO; | |
| 156 | ✗ | else if (strcasecmp(value, "warn") == 0) | |
| 157 | ✗ | *level = LOG_LEVEL_WARN; | |
| 158 | ✗ | else if (strcasecmp(value, "error") == 0) | |
| 159 | ✗ | *level = LOG_LEVEL_ERROR; | |
| 160 | else | ||
| 161 | ✗ | return -1; | |
| 162 | 9 | return 0; | |
| 163 | } | ||
| 164 | |||
| 165 | 9 | static int parse_log_format(const char *value, LogFormat *format) | |
| 166 | { | ||
| 167 |
1/2✓ Branch 0 taken 9 times.
✗ Branch 1 not taken.
|
9 | if (strcasecmp(value, "plain") == 0) |
| 168 | 9 | *format = LOG_FORMAT_PLAIN; | |
| 169 | ✗ | else if (strcasecmp(value, "json") == 0) | |
| 170 | ✗ | *format = LOG_FORMAT_JSON; | |
| 171 | else | ||
| 172 | ✗ | return -1; | |
| 173 | 9 | return 0; | |
| 174 | } | ||
| 175 | |||
| 176 | 9 | static int parse_appender_flags(yaml_document_t *doc, yaml_node_t *node, LoggingConfig *log_cfg) | |
| 177 | { | ||
| 178 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 9 times.
|
9 | if (!node) |
| 179 | ✗ | return 0; | |
| 180 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 9 times.
|
9 | if (node->type != YAML_SEQUENCE_NODE) { |
| 181 | ✗ | fprintf(stderr, "Invalid 'logging.appender_flags': expected sequence\n"); | |
| 182 | ✗ | return -1; | |
| 183 | } | ||
| 184 | |||
| 185 | 9 | log_cfg->appender_flags = 0; | |
| 186 | 9 | for (yaml_node_item_t *item = node->data.sequence.items.start; | |
| 187 |
2/2✓ Branch 0 taken 18 times.
✓ Branch 1 taken 9 times.
|
27 | item < node->data.sequence.items.top; item++) { |
| 188 | 18 | yaml_node_t *flag_node = yaml_document_get_node(doc, *item); | |
| 189 |
2/4✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 18 times.
|
18 | if (!flag_node || flag_node->type != YAML_SCALAR_NODE) { |
| 190 | ✗ | fprintf(stderr, "Invalid 'logging.appender_flags': entries must be scalars\n"); | |
| 191 | ✗ | return -1; | |
| 192 | } | ||
| 193 | 18 | const char *flag = (const char *)flag_node->data.scalar.value; | |
| 194 |
2/2✓ Branch 0 taken 9 times.
✓ Branch 1 taken 9 times.
|
18 | if (strcasecmp(flag, "file") == 0) |
| 195 | 9 | log_cfg->appender_flags |= APPENDER_FILE; | |
| 196 |
1/2✓ Branch 0 taken 9 times.
✗ Branch 1 not taken.
|
9 | else if (strcasecmp(flag, "console") == 0) |
| 197 | 9 | log_cfg->appender_flags |= APPENDER_CONSOLE; | |
| 198 | else { | ||
| 199 | ✗ | fprintf(stderr, "Invalid appender flag '%s': expected 'file' or 'console'\n", flag); | |
| 200 | ✗ | return -1; | |
| 201 | } | ||
| 202 | } | ||
| 203 | |||
| 204 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 9 times.
|
9 | if (log_cfg->appender_flags == 0) { |
| 205 | ✗ | fprintf(stderr, "Invalid 'logging.appender_flags': at least one appender required\n"); | |
| 206 | ✗ | return -1; | |
| 207 | } | ||
| 208 | 9 | return 0; | |
| 209 | } | ||
| 210 | |||
| 211 | 8 | static int validate_backend(const char *backend) | |
| 212 | { | ||
| 213 | char host[64]; | ||
| 214 | int port; | ||
| 215 | char trailing; | ||
| 216 | |||
| 217 |
2/4✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 8 times.
|
8 | if (!backend || backend[0] == '\0') |
| 218 | ✗ | return -1; | |
| 219 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 7 times.
|
8 | if (sscanf(backend, "%63[^:]:%d%c", host, &port, &trailing) != 2) |
| 220 | 1 | return -1; | |
| 221 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 7 times.
|
7 | if (host[0] == '\0') |
| 222 | ✗ | return -1; | |
| 223 |
2/4✓ Branch 0 taken 7 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 7 times.
|
7 | if (port < 1 || port > 65535) |
| 224 | ✗ | return -1; | |
| 225 | 7 | return 0; | |
| 226 | } | ||
| 227 | |||
| 228 | 12 | static int validate_routes(const ServerConfig *config) | |
| 229 | { | ||
| 230 |
2/2✓ Branch 0 taken 19 times.
✓ Branch 1 taken 9 times.
|
28 | for (int i = 0; i < config->route_count; i++) { |
| 231 | 19 | const Route *route = &config->routes[i]; | |
| 232 | |||
| 233 |
2/4✓ Branch 0 taken 19 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 19 times.
|
19 | if (route->path[0] == '\0' || route->path[0] != '/') { |
| 234 | ✗ | fprintf(stderr, "Invalid routes[%d].path: must start with '/'\n", i); | |
| 235 | ✗ | return -1; | |
| 236 | } | ||
| 237 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 19 times.
|
19 | if (route->technology[0] == '\0') { |
| 238 | ✗ | fprintf(stderr, "Invalid routes[%d].technology: required\n", i); | |
| 239 | ✗ | return -1; | |
| 240 | } | ||
| 241 | |||
| 242 |
2/2✓ Branch 0 taken 10 times.
✓ Branch 1 taken 9 times.
|
19 | if (strcmp(route->technology, "static") == 0) { |
| 243 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 9 times.
|
10 | if (route->document_root[0] == '\0') { |
| 244 | 1 | fprintf(stderr, "Invalid routes[%d].document_root: required for static route\n", i); | |
| 245 | 1 | return -1; | |
| 246 | } | ||
| 247 |
2/2✓ Branch 0 taken 8 times.
✓ Branch 1 taken 1 times.
|
9 | } else if (strcmp(route->technology, "reverse_proxy") == 0) { |
| 248 |
2/2✓ Branch 1 taken 1 times.
✓ Branch 2 taken 7 times.
|
8 | if (validate_backend(route->backend) != 0) { |
| 249 | 1 | fprintf(stderr, "Invalid routes[%d].backend: expected host:port\n", i); | |
| 250 | 1 | return -1; | |
| 251 | } | ||
| 252 | } else { | ||
| 253 | 1 | fprintf(stderr, "Invalid routes[%d].technology '%s': unsupported\n", i, route->technology); | |
| 254 | 1 | return -1; | |
| 255 | } | ||
| 256 | } | ||
| 257 | |||
| 258 | 9 | return 0; | |
| 259 | } | ||
| 260 | |||
| 261 | 13 | int load_config(ServerConfig *config, const char *file_path) | |
| 262 | { | ||
| 263 | 13 | FILE *fh = NULL; | |
| 264 | yaml_parser_t parser; | ||
| 265 | yaml_document_t document; | ||
| 266 | yaml_node_t *root; | ||
| 267 | yaml_node_t *node; | ||
| 268 | 13 | int parser_ready = 0; | |
| 269 | 13 | int document_ready = 0; | |
| 270 | 13 | int rc = -1; | |
| 271 | |||
| 272 |
2/4✓ Branch 0 taken 13 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 13 times.
|
13 | if (!config || !file_path) { |
| 273 | ✗ | fprintf(stderr, "Invalid input: config and file_path are required\n"); | |
| 274 | ✗ | return -1; | |
| 275 | } | ||
| 276 | |||
| 277 | 13 | set_config_defaults(config); | |
| 278 | |||
| 279 | 13 | fh = fopen(file_path, "rb"); | |
| 280 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 13 times.
|
13 | if (!fh) { |
| 281 | ✗ | fprintf(stderr, "Error opening configuration file: %s\n", file_path); | |
| 282 | ✗ | return -1; | |
| 283 | } | ||
| 284 | |||
| 285 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 13 times.
|
13 | if (!yaml_parser_initialize(&parser)) { |
| 286 | ✗ | fprintf(stderr, "Unable to initialize YAML parser\n"); | |
| 287 | ✗ | goto cleanup; | |
| 288 | } | ||
| 289 | 13 | parser_ready = 1; | |
| 290 | 13 | yaml_parser_set_input_file(&parser, fh); | |
| 291 | |||
| 292 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 13 times.
|
13 | if (!yaml_parser_load(&parser, &document)) { |
| 293 | ✗ | fprintf(stderr, "Error parsing YAML configuration file\n"); | |
| 294 | ✗ | goto cleanup; | |
| 295 | } | ||
| 296 | 13 | document_ready = 1; | |
| 297 | |||
| 298 | 13 | root = yaml_document_get_root_node(&document); | |
| 299 |
2/4✓ Branch 0 taken 13 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 13 times.
|
13 | if (!root || root->type != YAML_MAPPING_NODE) { |
| 300 | ✗ | fprintf(stderr, "YAML document is not a mapping\n"); | |
| 301 | ✗ | goto cleanup; | |
| 302 | } | ||
| 303 | |||
| 304 | 13 | node = find_yaml_node(&document, root, "server"); | |
| 305 |
2/2✓ Branch 0 taken 10 times.
✓ Branch 1 taken 3 times.
|
13 | if (node) { |
| 306 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 10 times.
|
10 | if (node->type != YAML_MAPPING_NODE) { |
| 307 | ✗ | fprintf(stderr, "Invalid 'server': expected mapping\n"); | |
| 308 | ✗ | goto cleanup; | |
| 309 | } | ||
| 310 | |||
| 311 | 10 | yaml_node_t *port_node = find_yaml_node(&document, node, "port"); | |
| 312 |
3/4✓ Branch 0 taken 10 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 1 times.
✓ Branch 4 taken 9 times.
|
10 | if (port_node && get_yaml_int_in_range(port_node, "server.port", 1, 65535, &config->port) != 0) |
| 313 | 1 | goto cleanup; | |
| 314 | |||
| 315 | 9 | yaml_node_t *max_conn_node = find_yaml_node(&document, node, "max_connections"); | |
| 316 |
2/4✓ Branch 0 taken 9 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 9 times.
|
18 | if (max_conn_node && |
| 317 | 9 | get_yaml_int_in_range(max_conn_node, "server.max_connections", 1, 1000000, | |
| 318 | &config->max_connections) != 0) | ||
| 319 | ✗ | goto cleanup; | |
| 320 | |||
| 321 | 9 | yaml_node_t *log_level_node = find_yaml_node(&document, node, "log_level"); | |
| 322 |
2/4✓ Branch 0 taken 9 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 9 times.
|
18 | if (log_level_node && |
| 323 | 9 | get_yaml_string(log_level_node, "server.log_level", | |
| 324 | 9 | config->log_level, sizeof(config->log_level)) != 0) | |
| 325 | ✗ | goto cleanup; | |
| 326 | } | ||
| 327 | |||
| 328 | 12 | node = find_yaml_node(&document, root, "logging"); | |
| 329 |
2/2✓ Branch 0 taken 9 times.
✓ Branch 1 taken 3 times.
|
12 | if (node) { |
| 330 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 9 times.
|
9 | if (node->type != YAML_MAPPING_NODE) { |
| 331 | ✗ | fprintf(stderr, "Invalid 'logging': expected mapping\n"); | |
| 332 | ✗ | goto cleanup; | |
| 333 | } | ||
| 334 | |||
| 335 | 9 | yaml_node_t *file_node = find_yaml_node(&document, node, "file"); | |
| 336 |
2/4✓ Branch 0 taken 9 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 9 times.
|
18 | if (file_node && |
| 337 | 9 | get_yaml_string(file_node, "logging.file", | |
| 338 | 9 | config->logging.file, sizeof(config->logging.file)) != 0) | |
| 339 | ✗ | goto cleanup; | |
| 340 | |||
| 341 | 9 | yaml_node_t *level_node = find_yaml_node(&document, node, "level"); | |
| 342 |
1/2✓ Branch 0 taken 9 times.
✗ Branch 1 not taken.
|
9 | if (level_node) { |
| 343 | char level_str[16]; | ||
| 344 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 9 times.
|
9 | if (get_yaml_string(level_node, "logging.level", level_str, sizeof(level_str)) != 0) |
| 345 | ✗ | goto cleanup; | |
| 346 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 9 times.
|
9 | if (parse_log_level(level_str, &config->logging.level) != 0) { |
| 347 | ✗ | fprintf(stderr, "Invalid 'logging.level': expected debug/info/warn/error\n"); | |
| 348 | ✗ | goto cleanup; | |
| 349 | } | ||
| 350 | } | ||
| 351 | |||
| 352 | 9 | yaml_node_t *format_node = find_yaml_node(&document, node, "format"); | |
| 353 |
1/2✓ Branch 0 taken 9 times.
✗ Branch 1 not taken.
|
9 | if (format_node) { |
| 354 | char format_str[16]; | ||
| 355 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 9 times.
|
9 | if (get_yaml_string(format_node, "logging.format", format_str, sizeof(format_str)) != 0) |
| 356 | ✗ | goto cleanup; | |
| 357 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 9 times.
|
9 | if (parse_log_format(format_str, &config->logging.format) != 0) { |
| 358 | ✗ | fprintf(stderr, "Invalid 'logging.format': expected plain/json\n"); | |
| 359 | ✗ | goto cleanup; | |
| 360 | } | ||
| 361 | } | ||
| 362 | |||
| 363 | 9 | yaml_node_t *buffer_size_node = find_yaml_node(&document, node, "buffer_size"); | |
| 364 |
2/4✓ Branch 0 taken 9 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 9 times.
|
18 | if (buffer_size_node && |
| 365 | 9 | get_yaml_size_in_range(buffer_size_node, "logging.buffer_size", 1, 1048576, | |
| 366 | &config->logging.buffer_size) != 0) | ||
| 367 | ✗ | goto cleanup; | |
| 368 | |||
| 369 | 9 | yaml_node_t *rollover_size_node = find_yaml_node(&document, node, "rollover_size"); | |
| 370 |
2/4✓ Branch 0 taken 9 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 9 times.
|
18 | if (rollover_size_node && |
| 371 | 9 | get_yaml_size_in_range(rollover_size_node, "logging.rollover_size", 0, 1099511627776ULL, | |
| 372 | &config->logging.rollover_size) != 0) | ||
| 373 | ✗ | goto cleanup; | |
| 374 | |||
| 375 | 9 | yaml_node_t *rollover_daily_node = find_yaml_node(&document, node, "rollover_daily"); | |
| 376 |
2/4✓ Branch 0 taken 9 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 9 times.
|
18 | if (rollover_daily_node && |
| 377 | 9 | get_yaml_bool(rollover_daily_node, "logging.rollover_daily", | |
| 378 | &config->logging.rollover_daily) != 0) | ||
| 379 | ✗ | goto cleanup; | |
| 380 | |||
| 381 | 9 | yaml_node_t *appender_flags_node = find_yaml_node(&document, node, "appender_flags"); | |
| 382 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 9 times.
|
9 | if (parse_appender_flags(&document, appender_flags_node, &config->logging) != 0) |
| 383 | ✗ | goto cleanup; | |
| 384 | } | ||
| 385 | |||
| 386 | 12 | node = find_yaml_node(&document, root, "ssl"); | |
| 387 |
1/2✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
|
12 | if (node) { |
| 388 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
|
12 | if (node->type != YAML_MAPPING_NODE) { |
| 389 | ✗ | fprintf(stderr, "Invalid 'ssl': expected mapping\n"); | |
| 390 | ✗ | goto cleanup; | |
| 391 | } | ||
| 392 | |||
| 393 | 12 | yaml_node_t *cert_node = find_yaml_node(&document, node, "certificate"); | |
| 394 |
2/4✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 12 times.
|
24 | if (cert_node && |
| 395 | 12 | get_yaml_string(cert_node, "ssl.certificate", | |
| 396 | 12 | config->ssl.certificate, sizeof(config->ssl.certificate)) != 0) | |
| 397 | ✗ | goto cleanup; | |
| 398 | |||
| 399 | 12 | yaml_node_t *key_node = find_yaml_node(&document, node, "private_key"); | |
| 400 |
2/4✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 12 times.
|
24 | if (key_node && |
| 401 | 12 | get_yaml_string(key_node, "ssl.private_key", | |
| 402 | 12 | config->ssl.private_key, sizeof(config->ssl.private_key)) != 0) | |
| 403 | ✗ | goto cleanup; | |
| 404 | } | ||
| 405 | |||
| 406 | 12 | node = find_yaml_node(&document, root, "routes"); | |
| 407 |
1/2✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
|
12 | if (node) { |
| 408 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
|
12 | if (node->type != YAML_SEQUENCE_NODE) { |
| 409 | ✗ | fprintf(stderr, "Invalid 'routes': expected sequence\n"); | |
| 410 | ✗ | goto cleanup; | |
| 411 | } | ||
| 412 | |||
| 413 | 12 | for (yaml_node_item_t *item = node->data.sequence.items.start; | |
| 414 |
2/2✓ Branch 0 taken 19 times.
✓ Branch 1 taken 12 times.
|
31 | item < node->data.sequence.items.top; item++) { |
| 415 | 19 | yaml_node_t *route_node = yaml_document_get_node(&document, *item); | |
| 416 | Route *route; | ||
| 417 | yaml_node_t *route_field; | ||
| 418 | |||
| 419 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 19 times.
|
19 | if (config->route_count >= MAX_ROUTES) { |
| 420 | ✗ | fprintf(stderr, "Too many routes: maximum supported is %d\n", MAX_ROUTES); | |
| 421 | ✗ | goto cleanup; | |
| 422 | } | ||
| 423 |
2/4✓ Branch 0 taken 19 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 19 times.
|
19 | if (!route_node || route_node->type != YAML_MAPPING_NODE) { |
| 424 | ✗ | fprintf(stderr, "Invalid route entry: expected mapping\n"); | |
| 425 | ✗ | goto cleanup; | |
| 426 | } | ||
| 427 | |||
| 428 | 19 | route = &config->routes[config->route_count]; | |
| 429 | 19 | memset(route, 0, sizeof(*route)); | |
| 430 | |||
| 431 | 19 | route_field = find_yaml_node(&document, route_node, "path"); | |
| 432 |
2/4✓ Branch 0 taken 19 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 19 times.
|
38 | if (!route_field || |
| 433 | 19 | get_yaml_string(route_field, "routes[].path", | |
| 434 | 19 | route->path, sizeof(route->path)) != 0) { | |
| 435 | ✗ | fprintf(stderr, "Invalid route: missing or invalid 'path'\n"); | |
| 436 | ✗ | goto cleanup; | |
| 437 | } | ||
| 438 | |||
| 439 | 19 | route_field = find_yaml_node(&document, route_node, "technology"); | |
| 440 |
2/4✓ Branch 0 taken 19 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 19 times.
|
38 | if (!route_field || |
| 441 | 19 | get_yaml_string(route_field, "routes[].technology", | |
| 442 | 19 | route->technology, sizeof(route->technology)) != 0) { | |
| 443 | ✗ | fprintf(stderr, "Invalid route: missing or invalid 'technology'\n"); | |
| 444 | ✗ | goto cleanup; | |
| 445 | } | ||
| 446 | |||
| 447 | 19 | route_field = find_yaml_node(&document, route_node, "document_root"); | |
| 448 |
3/4✓ Branch 0 taken 9 times.
✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 9 times.
|
28 | if (route_field && |
| 449 | 9 | get_yaml_string(route_field, "routes[].document_root", | |
| 450 | 9 | route->document_root, sizeof(route->document_root)) != 0) | |
| 451 | ✗ | goto cleanup; | |
| 452 | |||
| 453 | 19 | route_field = find_yaml_node(&document, route_node, "backend"); | |
| 454 |
3/4✓ Branch 0 taken 9 times.
✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 9 times.
|
28 | if (route_field && |
| 455 | 9 | get_yaml_string(route_field, "routes[].backend", | |
| 456 | 9 | route->backend, sizeof(route->backend)) != 0) | |
| 457 | ✗ | goto cleanup; | |
| 458 | |||
| 459 | 19 | config->route_count++; | |
| 460 | } | ||
| 461 | } | ||
| 462 | |||
| 463 |
2/4✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 12 times.
|
12 | if (config->ssl.certificate[0] == '\0' || config->ssl.private_key[0] == '\0') { |
| 464 | ✗ | fprintf(stderr, "Invalid SSL config: certificate and private_key are required\n"); | |
| 465 | ✗ | goto cleanup; | |
| 466 | } | ||
| 467 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
|
12 | if (config->logging.appender_flags == 0) { |
| 468 | ✗ | fprintf(stderr, "Invalid logging config: at least one appender must be enabled\n"); | |
| 469 | ✗ | goto cleanup; | |
| 470 | } | ||
| 471 |
2/4✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 12 times.
|
12 | if ((config->logging.appender_flags & APPENDER_FILE) && config->logging.file[0] == '\0') { |
| 472 | ✗ | fprintf(stderr, "Invalid logging config: file appender requires 'logging.file'\n"); | |
| 473 | ✗ | goto cleanup; | |
| 474 | } | ||
| 475 |
2/2✓ Branch 1 taken 3 times.
✓ Branch 2 taken 9 times.
|
12 | if (validate_routes(config) != 0) |
| 476 | 3 | goto cleanup; | |
| 477 | |||
| 478 | 9 | rc = 0; | |
| 479 | |||
| 480 | 13 | cleanup: | |
| 481 |
1/2✓ Branch 0 taken 13 times.
✗ Branch 1 not taken.
|
13 | if (document_ready) |
| 482 | 13 | yaml_document_delete(&document); | |
| 483 |
1/2✓ Branch 0 taken 13 times.
✗ Branch 1 not taken.
|
13 | if (parser_ready) |
| 484 | 13 | yaml_parser_delete(&parser); | |
| 485 |
1/2✓ Branch 0 taken 13 times.
✗ Branch 1 not taken.
|
13 | if (fh) |
| 486 | 13 | fclose(fh); | |
| 487 | 13 | return rc; | |
| 488 | } | ||
| 489 |