emme coverage


Directory: src/
File: src/config.c
Date: 2025-08-24 07:42:18
Exec Total Coverage
Lines: 76 147 51.7%
Functions: 4 5 80.0%
Branches: 40 112 35.7%

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