Line data Source code
1 : /*
2 : * SPDX license identifier: MPL-2.0
3 : *
4 : * Copyright (C) 2015, Advanced Driver Information Technology
5 : * This code is developed by Advanced Driver Information Technology.
6 : * Copyright of Advanced Driver Information Technology, Bosch and DENSO.
7 : *
8 : * This file is part of COVESA Project DLT - Diagnostic Log and Trace.
9 : *
10 : * This Source Code Form is subject to the terms of the
11 : * Mozilla Public License (MPL), v. 2.0.
12 : * If a copy of the MPL was not distributed with this file,
13 : * You can obtain one at http://mozilla.org/MPL/2.0/.
14 : *
15 : * For further information see http://www.covesa.org/.
16 : */
17 :
18 : /*!
19 : * \author
20 : * Christoph Lipka <clipka@jp.adit-jv.com>
21 : *
22 : * \copyright Copyright © 2015 Advanced Driver Information Technology. \n
23 : * License MPL-2.0: Mozilla Public License version 2.0 http://mozilla.org/MPL/2.0/.
24 : *
25 : * \file dlt_config_file_parser.c
26 : */
27 :
28 : #include "dlt_config_file_parser.h"
29 : #include <stdlib.h>
30 : #include <stdio.h>
31 : #include <string.h>
32 : #include <ctype.h>
33 : #include <syslog.h>
34 : #include "dlt_common.h"
35 : #include "dlt_log.h"
36 : #include "dlt-daemon_cfg.h"
37 :
38 : /* internal defines */
39 : #define DLT_CONFIG_FILE_NEW_SECTION 0x0a
40 : #define DLT_CONFIG_FILE_NEW_DATA 0x0b
41 :
42 :
43 : /* internal helper functions */
44 :
45 : /**
46 : * dlt_config_file_trim_line
47 : *
48 : * Trim all whitespace from a string
49 : *
50 : * @param line String to remove whitespace from
51 : */
52 175 : static void dlt_config_file_trim_line(char* line)
53 : {
54 175 : if (line == NULL)
55 : return;
56 :
57 : char* i = line;
58 : char* j = line;
59 :
60 3046 : while (*j != '\0') {
61 2871 : *i = *j++;
62 :
63 2871 : if (!isspace(*i))
64 2696 : i++;
65 : }
66 :
67 175 : *i = '\0';
68 : }
69 :
70 : /**
71 : * dlt_config_file_ignore_line
72 : *
73 : * Check if a line has to be ignored, because it contains a comment or is empty
74 : *
75 : * @param line Line of configuration file
76 : * @return 0 if ignore, -1 do not ignore
77 : */
78 : static int dlt_config_file_ignore_line(char* line)
79 : {
80 186 : if ((line[0] == '#') || (line[0] == ';') || (line[0] == '\n') || (line[0] == '\0'))
81 : return 0; /* ignore */
82 : else
83 : return -1; /* do not ignore */
84 : }
85 :
86 : /**
87 : * dlt_config_file_is_section_name
88 : *
89 : * Check if section name already used
90 : *
91 : * @param file DltConfigFile
92 : * @param name Name of section
93 : * @return 0, section name not used, -1 section name already used
94 : */
95 19 : static int dlt_config_file_is_section_name(DltConfigFile* file, char* name)
96 : {
97 : int i = 0;
98 :
99 19 : if ((file == NULL) || (name == NULL))
100 : return -1;
101 :
102 85 : for (i = 0; i < file->num_sections; i++) {
103 66 : DltConfigFileSection* s = &file->sections[i];
104 :
105 66 : if (strncmp(s->name, name, DLT_CONFIG_FILE_ENTRY_MAX_LEN) == 0)
106 : return -1;
107 : }
108 :
109 : return 0; /* section name not used */
110 : }
111 :
112 : /**
113 : * dlt_config_file_set_section
114 : *
115 : * Store section in internal data structure
116 : *
117 : * @param file DltConfigFile
118 : * @param name Name of section
119 : * @return 0 on success, else -1
120 : */
121 19 : static int dlt_config_file_set_section(DltConfigFile* file, char* name)
122 : {
123 19 : int section = file->num_sections;
124 :
125 : /* check if adding another section would exceed max number of sections */
126 19 : if (section >= DLT_CONFIG_FILE_SECTIONS_MAX) {
127 0 : dlt_log(LOG_WARNING, "Cannot store more sections\n");
128 0 : return -1; /* reached max number of sections */
129 : }
130 :
131 : /* do not store section with same name again */
132 19 : if (dlt_config_file_is_section_name(file, name) != 0) {
133 0 : dlt_log(LOG_WARNING, "Cannot store section name again\n");
134 0 : return -1;
135 : }
136 :
137 19 : DltConfigFileSection* s = &file->sections[section];
138 :
139 : /* alloc data for entries */
140 19 : s->name = calloc(DLT_CONFIG_FILE_ENTRY_MAX_LEN + 1, sizeof(char));
141 :
142 19 : if (s->name == NULL) {
143 0 : dlt_log(LOG_ERR, "Cannot allocate memory for internal data structure\n");
144 0 : return -1;
145 : }
146 :
147 19 : s->keys = calloc(DLT_CONFIG_FILE_ENTRY_MAX_LEN * DLT_CONFIG_FILE_KEYS_MAX + 1, sizeof(char));
148 :
149 19 : if (s->keys == NULL) {
150 0 : free(s->name);
151 0 : s->name = NULL;
152 0 : dlt_log(LOG_ERR, "Cannot allocate memory for internal data structure\n");
153 0 : return -1;
154 : }
155 :
156 : strncpy(file->sections[section].name, name, DLT_CONFIG_FILE_ENTRY_MAX_LEN);
157 19 : file->num_sections += 1;
158 19 : return 0;
159 : }
160 :
161 : /**
162 : * dlt_config_file_set_section_data
163 : *
164 : * Store data pair of a section
165 : *
166 : * @param file DltConfigFile
167 : * @param str1 string used for key
168 : * @param str2 string used for value
169 : * @return 0 on success, else -1
170 : */
171 156 : static int dlt_config_file_set_section_data(DltConfigFile* file, char* str1, char* str2)
172 : {
173 : DltConfigKeyData** tmp = NULL;
174 :
175 156 : if ((file == NULL) || (str1 == NULL) || (str2 == NULL))
176 : return -1;
177 :
178 156 : DltConfigFileSection* s = &file->sections[file->num_sections - 1];
179 156 : int key_number = s->num_entries;
180 :
181 156 : if (key_number + 1 >= DLT_CONFIG_FILE_KEYS_MAX) {
182 0 : dlt_log(LOG_WARNING, "Cannot store more keys in section\n");
183 0 : return -1; /* reached max number of keys per section */
184 : }
185 :
186 : /* copy data into structure */
187 156 : strncpy(&s->keys[key_number * DLT_CONFIG_FILE_ENTRY_MAX_LEN], str1, DLT_CONFIG_FILE_ENTRY_MAX_LEN);
188 :
189 156 : if (s->list == NULL) {
190 : /* creating a list if it doesnt exists */
191 19 : s->list = malloc(sizeof(DltConfigKeyData));
192 :
193 19 : if (s->list == NULL) {
194 0 : dlt_log(LOG_WARNING, "Could not allocate initial memory to list \n");
195 0 : return -1;
196 : }
197 :
198 19 : tmp = &s->list;
199 : } else {
200 137 : tmp = &s->list;
201 :
202 715 : while (*(tmp) != NULL)
203 578 : tmp = &(*tmp)->next;
204 :
205 : /* Adding new entry to the list */
206 137 : *tmp = malloc(sizeof(DltConfigKeyData));
207 :
208 137 : if (*tmp == NULL) {
209 0 : dlt_log(LOG_WARNING, "Could not allocate memory to list \n");
210 0 : return -1;
211 : }
212 : }
213 :
214 156 : (*tmp)->key = strdup(str1);
215 156 : (*tmp)->data = strdup(str2);
216 156 : (*tmp)->next = NULL;
217 :
218 156 : s->num_entries += 1;
219 :
220 156 : return 0;
221 : }
222 :
223 : /**
224 : * dlt_config_file_has_section
225 : *
226 : * Check if a certain line in config file is a section header
227 : *
228 : * @param line Line in configuration file
229 : * @return 0 if section header, else -1
230 : */
231 : static int dlt_config_file_line_has_section(char* line)
232 : {
233 : (void)line; /* avoid compiler warnings */
234 :
235 175 : if (line[0] == '[') /* section found */
236 : return 0;
237 : else
238 : return -1;
239 : }
240 :
241 : /**
242 : * dlt_config_file_get_section_name_from_string
243 : *
244 : * Extract section name from line
245 : *
246 : * @param line Line in configuration file containing a section header
247 : * @param name Section name
248 : * @return 0 on success, else -1
249 : */
250 19 : static int dlt_config_file_get_section_name_from_string(char* line, char* name)
251 : {
252 : int i = 0;
253 : int j = 0;
254 :
255 19 : if ((line == NULL) || (name == NULL))
256 : return -1;
257 :
258 189 : for (i = 0; i < DLT_CONFIG_FILE_ENTRY_MAX_LEN; i++) {
259 189 : if ((line[i] == '[') || isspace(line[i]))
260 19 : continue;
261 170 : else if ((line[i] == ']') || (line[i] == '\n') || (line[i] == '\0'))
262 : break;
263 : else
264 151 : name[j++] = line[i];
265 : }
266 :
267 : return 0;
268 : }
269 :
270 : /**
271 : * dlt_config_file_get_key_value
272 : *
273 : * Get key and value from a line of configuration file
274 : *
275 : * @param line Line on configuration file
276 : * @param[out] str1 String to be used as key
277 : * @param[out] str2 String to be used as value
278 : * @return 0 on success, else -1
279 : */
280 156 : static int dlt_config_file_get_key_value(char* line, char* str1, char* str2)
281 : {
282 : char* delimiter = "=";
283 : char* ptr;
284 : char* save_ptr;
285 :
286 156 : if ((line == NULL) || (str1 == NULL) || (str2 == NULL))
287 : return -1;
288 :
289 156 : ptr = strtok_r(line, delimiter, &save_ptr);
290 :
291 156 : if (ptr != NULL) { /* get key */
292 : strncpy(str1, ptr, DLT_CONFIG_FILE_ENTRY_MAX_LEN - 1);
293 156 : str1[DLT_CONFIG_FILE_ENTRY_MAX_LEN - 1] = '\0';
294 : } else {
295 : return -1;
296 : }
297 :
298 156 : ptr = strtok_r(NULL, delimiter, &save_ptr);
299 :
300 156 : if (ptr != NULL) {
301 : strncpy(str2, ptr, DLT_CONFIG_FILE_ENTRY_MAX_LEN - 1);
302 156 : str2[DLT_CONFIG_FILE_ENTRY_MAX_LEN - 1] = '\0';
303 : } else {
304 : return -1;
305 : }
306 :
307 156 : return 0;
308 : }
309 :
310 : /**
311 : * dlt_config_file_read_line
312 : *
313 : * Read line from configuration file
314 : *
315 : * @param line Line from configuration file
316 : * @param[out] str1 String contains section header or key
317 : * @param[out] str2 String contains value or is empty
318 : * @return 0 on success, else -1
319 : */
320 175 : static int dlt_config_file_read_line(char* line, char* str1, char* str2)
321 : {
322 175 : if ((line == NULL) || (str1 == NULL) || (str2 == NULL))
323 : return -1;
324 :
325 : /* reset values to zero */
326 : memset(str1, 0, DLT_CONFIG_FILE_ENTRY_MAX_LEN);
327 : memset(str2, 0, DLT_CONFIG_FILE_ENTRY_MAX_LEN);
328 :
329 : /* check if line contains a section */
330 : if ((dlt_config_file_line_has_section(line)) == 0) {
331 : /* retrieve section name */
332 19 : if (dlt_config_file_get_section_name_from_string(line, str1) != 0)
333 : return -1;
334 :
335 : return DLT_CONFIG_FILE_NEW_SECTION;
336 : }
337 :
338 : /* copy strings as key value pair into str1, str2 */
339 156 : if (dlt_config_file_get_key_value(line, str1, str2) != 0)
340 : return -1;
341 :
342 : return DLT_CONFIG_FILE_NEW_DATA;
343 : }
344 :
345 : /**
346 : * dlt_config_file_read_file
347 : *
348 : * Read configuration file line by line and fill internal structures
349 : *
350 : * @param file DltConfigFile
351 : * @param hdl FILE handle of opened configuration file
352 : */
353 8 : static void dlt_config_file_read_file(DltConfigFile* file, FILE* hdl)
354 : {
355 : int ret = 0;
356 8 : char line[DLT_CONFIG_FILE_LINE_MAX_LEN] = {'\0'};
357 8 : char str1[DLT_CONFIG_FILE_ENTRY_MAX_LEN] = {'\0'};
358 8 : char str2[DLT_CONFIG_FILE_ENTRY_MAX_LEN] = {'\0'};
359 : int line_number = 0;
360 : int is_section_valid = -1; /* to check if section name is given twice or invalid */
361 :
362 : /* read configuration file line by line */
363 202 : while (fgets(line, DLT_CONFIG_FILE_LINE_MAX_LEN, hdl) != NULL) {
364 186 : line_number++;
365 :
366 : /* ignore empty and comment lines */
367 11 : if (dlt_config_file_ignore_line(line) == 0)
368 11 : continue;
369 :
370 : /* trim line end */
371 175 : dlt_config_file_trim_line(line);
372 :
373 : /* parse content of line */
374 175 : ret = dlt_config_file_read_line(line, str1, str2);
375 :
376 175 : switch (ret) {
377 19 : case DLT_CONFIG_FILE_NEW_SECTION: /* store str1 as new section */
378 : is_section_valid = -1;
379 :
380 19 : if ((ret = dlt_config_file_set_section(file, str1)) == 0)
381 : is_section_valid = 0;
382 :
383 : break;
384 156 : case DLT_CONFIG_FILE_NEW_DATA: /* store str1 and str2 as new data for section */
385 :
386 156 : if (is_section_valid == 0)
387 156 : dlt_config_file_set_section_data(file, str1, str2);
388 :
389 : break;
390 0 : default: /* something is wrong with the line */
391 0 : dlt_vlog(LOG_WARNING, "Line (%d) \"%s\" is invalid\n", line_number, line);
392 : }
393 : }
394 8 : }
395 :
396 : /**
397 : * dlt_config_file_find_section
398 : *
399 : * Find a section
400 : *
401 : * @param file DltConfigFile
402 : * @param section Name of section
403 : * @return number of section on success, else -1
404 : */
405 251 : static int dlt_config_file_find_section(const DltConfigFile* file, const char* section)
406 : {
407 : int i = 0;
408 :
409 251 : if ((file == NULL) || (section == NULL) || (file->num_sections <= 0)) {
410 0 : dlt_log(LOG_WARNING, "Section cannot be found due to invalid parameters\n");
411 0 : return -1;
412 : }
413 :
414 1178 : for (i = 0; i < file->num_sections; i++) {
415 1175 : DltConfigFileSection* s = &file->sections[i];
416 :
417 1175 : if (strncmp(s->name, section, DLT_CONFIG_FILE_ENTRY_MAX_LEN) == 0)
418 248 : return i;
419 : }
420 :
421 : return -1;
422 : }
423 :
424 : /************************** interface implementation ***************************/
425 8 : DltConfigFile* dlt_config_file_init(char* file_name)
426 : {
427 : DltConfigFile* file;
428 : FILE* hdl = NULL;
429 :
430 8 : if ((file_name == NULL) || (strlen(file_name) >= DLT_PATH_MAX)) {
431 0 : dlt_log(LOG_ERR, "Given configuration file invalid\n");
432 0 : return NULL;
433 : }
434 :
435 8 : file = calloc(1, sizeof(DltConfigFile));
436 :
437 8 : if (file == NULL) {
438 0 : dlt_log(LOG_ERR, "Setup internal data structure to parse config file failed\n");
439 0 : return NULL;
440 : }
441 :
442 8 : file->sections = calloc(DLT_CONFIG_FILE_SECTIONS_MAX, sizeof(DltConfigFileSection));
443 :
444 : /* open file */
445 8 : if ((hdl = fopen(file_name, "r")) == NULL) {
446 0 : dlt_log(LOG_ERR, "Cannot open configuration file\n");
447 0 : free(file);
448 0 : return NULL;
449 : }
450 :
451 8 : dlt_config_file_read_file(file, hdl);
452 :
453 : /* all information stored internally */
454 8 : fclose(hdl);
455 :
456 8 : return file;
457 : }
458 :
459 8 : void dlt_config_file_release(DltConfigFile* file)
460 : {
461 : int i = 0;
462 :
463 8 : if (file != NULL) {
464 8 : int max = file->num_sections;
465 :
466 27 : for (i = 0; i < max; i++) {
467 19 : DltConfigFileSection* s = &file->sections[i];
468 19 : DltConfigKeyData* node = file->sections[i].list;
469 19 : free(s->name);
470 :
471 19 : if (s->keys != NULL)
472 19 : free(s->keys);
473 :
474 175 : while (node) {
475 : DltConfigKeyData* tmp = node;
476 156 : node = node->next;
477 156 : free(tmp->key);
478 156 : free(tmp->data);
479 156 : free(tmp);
480 : }
481 : }
482 :
483 8 : free(file->sections);
484 8 : free(file);
485 : }
486 8 : }
487 :
488 19 : int dlt_config_file_get_section_name(const DltConfigFile* file, int num, char* name)
489 : {
490 19 : if ((file == NULL) || (name == NULL) || (num < 0) || (num >= file->num_sections))
491 : return -1;
492 :
493 19 : strncpy(name, (file->sections + num)->name, DLT_CONFIG_FILE_ENTRY_MAX_LEN);
494 19 : name[DLT_CONFIG_FILE_ENTRY_MAX_LEN - 1] = '\0';
495 :
496 19 : return 0;
497 : }
498 :
499 8 : int dlt_config_file_get_num_sections(const DltConfigFile* file, int* num)
500 : {
501 8 : if ((file == NULL) || (file->num_sections < 0))
502 : return -1;
503 :
504 : /*
505 : * Note: Since General section could be used in configuration file,
506 : * this number could be also containing General section.
507 : */
508 8 : *num = file->num_sections;
509 :
510 8 : return 0;
511 : }
512 :
513 248 : int dlt_config_file_get_value(const DltConfigFile* file, const char* section, const char* key, char* value)
514 : {
515 : DltConfigFileSection* s = NULL;
516 : DltConfigKeyData** tmp = NULL;
517 : int num_section = 0;
518 :
519 248 : if ((file == NULL) || (section == NULL) || (key == NULL) || (value == NULL))
520 : return -1;
521 :
522 : /* clean value */
523 : memset(value, 0, DLT_CONFIG_FILE_ENTRY_MAX_LEN);
524 :
525 248 : num_section = dlt_config_file_find_section(file, section);
526 :
527 248 : if (num_section == -1)
528 : return -1;
529 :
530 248 : s = (file->sections + num_section);
531 :
532 248 : tmp = &s->list;
533 :
534 1557 : while (*(tmp) != NULL) {
535 1462 : if (strncmp((*tmp)->key, key, DLT_CONFIG_FILE_ENTRY_MAX_LEN) == 0) {
536 153 : strncpy(value, (*tmp)->data, DLT_CONFIG_FILE_ENTRY_MAX_LEN);
537 153 : return 0;
538 : } else { /* not found yet see list for more */
539 1309 : tmp = &(*tmp)->next;
540 : }
541 : }
542 :
543 95 : dlt_vlog(LOG_WARNING, "Entry does not exist in section: %s\n", key);
544 95 : return -1;
545 : }
546 :
547 3 : int dlt_config_file_check_section_name_exists(const DltConfigFile* file, const char* name)
548 : {
549 : int ret = 0;
550 :
551 3 : if ((file == NULL) || (file->num_sections <= 0) || (name == NULL))
552 : return -1;
553 :
554 3 : ret = dlt_config_file_find_section(file, name);
555 3 : if (ret == -1)
556 : return ret;
557 :
558 : return 0;
559 : }
|