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