Line data Source code
1 : /*
2 : INI LIBRARY
3 :
4 : Reading configuration from INI file
5 : and storing as a collection.
6 :
7 : Copyright (C) Dmitri Pal <dpal@redhat.com> 2009
8 :
9 : INI Library is free software: you can redistribute it and/or modify
10 : it under the terms of the GNU Lesser General Public License as published by
11 : the Free Software Foundation, either version 3 of the License, or
12 : (at your option) any later version.
13 :
14 : INI Library is distributed in the hope that it will be useful,
15 : but WITHOUT ANY WARRANTY; without even the implied warranty of
16 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 : GNU Lesser General Public License for more details.
18 :
19 : You should have received a copy of the GNU Lesser General Public License
20 : along with INI Library. If not, see <http://www.gnu.org/licenses/>.
21 : */
22 :
23 : #include "config.h"
24 : #include <errno.h>
25 : #include <string.h>
26 : #include <stdlib.h>
27 : #include <ctype.h>
28 : #include "trace.h"
29 : #include "collection.h"
30 : #include "collection_tools.h"
31 : #include "path_utils.h"
32 : #include "ini_defines.h"
33 : #include "ini_metadata.h"
34 : #include "ini_config.h"
35 :
36 : /* Temporarily move the parsing function here */
37 : /* THIS FUNCTION WILL BE REMOVED AS SOON AS WE SWITCH TO THE NEW INTERFACE */
38 : /* Reads a line from the file */
39 651 : static int read_line(FILE *file,
40 : char *buf,
41 : int read_size,
42 : char **key, char **value,
43 : int *length,
44 : int *ext_error)
45 : {
46 :
47 : char *res;
48 : int len;
49 : char *buffer;
50 : int i;
51 : char *eq;
52 :
53 : TRACE_FLOW_STRING("read_line", "Entry");
54 :
55 651 : *ext_error = 0;
56 :
57 651 : buffer = buf;
58 :
59 : /* Get data from file */
60 651 : res = fgets(buffer, read_size - 1, file);
61 651 : if (res == NULL) {
62 : TRACE_ERROR_STRING("Read nothing", "");
63 : return RET_EOF;
64 : }
65 :
66 : /* Make sure the buffer is NULL terminated */
67 640 : buffer[read_size - 1] = '\0';
68 :
69 640 : len = strlen(buffer);
70 640 : if (len == 0) {
71 : TRACE_ERROR_STRING("Nothing was read.", "");
72 : return RET_EMPTY;
73 : }
74 :
75 : /* Added \r just in case we deal with Windows in future */
76 640 : if ((buffer[len - 1] != '\n') && (buffer[len - 1] != '\r')) {
77 : TRACE_ERROR_STRING("String it too big!", "");
78 0 : *ext_error = ERR_LONGDATA;
79 0 : return RET_ERROR;
80 : }
81 :
82 : /* Ingnore comments */
83 640 : if ((*buffer == ';') || (*buffer == '#')) {
84 : TRACE_FLOW_STRING("Comment", buf);
85 : return RET_COMMENT;
86 : }
87 :
88 : TRACE_INFO_STRING("BUFFER before trimming:", buffer);
89 :
90 : /* Trucate trailing spaces and CRs */
91 : /* Make sure not to step before the beginning */
92 1644 : while (len && isspace(buffer[len - 1])) {
93 1027 : buffer[len - 1] = '\0';
94 1027 : len--;
95 : }
96 :
97 : TRACE_INFO_STRING("BUFFER after trimming trailing spaces:", buffer);
98 :
99 : /* Trucate leading spaces */
100 776 : while (isspace(*buffer)) {
101 159 : buffer++;
102 159 : len--;
103 : }
104 :
105 : TRACE_INFO_STRING("BUFFER after trimming leading spaces:", buffer);
106 : TRACE_INFO_NUMBER("BUFFER length:", len);
107 :
108 : /* Empty line */
109 617 : if (len == 0) {
110 : TRACE_FLOW_STRING("Empty line", buf);
111 : return RET_EMPTY;
112 : }
113 :
114 : /* Section */
115 408 : if (*buffer == '[') {
116 75 : if (buffer[len-1] != ']') {
117 : TRACE_ERROR_STRING("Invalid format for section", buf);
118 14 : *ext_error = ERR_NOCLOSESEC;
119 14 : return RET_ERROR;
120 : }
121 61 : buffer++;
122 61 : len--;
123 489 : while (isspace(*buffer)) {
124 367 : buffer++;
125 367 : len--;
126 : }
127 61 : if (len == 0) {
128 : TRACE_ERROR_STRING("Invalid format for section", buf);
129 0 : *ext_error = ERR_NOSECTION;
130 0 : return RET_ERROR;
131 : }
132 :
133 61 : buffer[len - 1] = '\0';
134 61 : len--;
135 297 : while (isspace(buffer[len - 1])) {
136 175 : buffer[len - 1] = '\0';
137 175 : len--;
138 : }
139 61 : if (len >= MAX_KEY) {
140 : TRACE_ERROR_STRING("Section name is too long", buf);
141 7 : *ext_error = ERR_SECTIONLONG;
142 7 : return RET_ERROR;
143 : }
144 :
145 54 : *key = buffer;
146 54 : return RET_SECTION;
147 : }
148 :
149 : /* Assume we are dealing with the K-V here */
150 : /* Find "=" */
151 333 : eq = strchr(buffer, '=');
152 333 : if (eq == NULL) {
153 : TRACE_ERROR_STRING("No equal sign", buf);
154 38 : *ext_error = ERR_NOEQUAL;
155 38 : return RET_INVALID;
156 : }
157 :
158 295 : len -= eq-buffer;
159 :
160 : /* Strip spaces around "=" */
161 295 : i = eq - buffer - 1;
162 295 : while ((i >= 0) && isspace(buffer[i])) i--;
163 295 : if (i < 0) {
164 : TRACE_ERROR_STRING("No key", buf);
165 8 : *ext_error = ERR_NOKEY;
166 8 : return RET_INVALID;
167 : }
168 :
169 : /* Copy key into provided buffer */
170 287 : if(i >= MAX_KEY) {
171 : TRACE_ERROR_STRING("Key name is too long", buf);
172 7 : *ext_error = ERR_LONGKEY;
173 7 : return RET_INVALID;
174 : }
175 280 : *key = buffer;
176 280 : buffer[i + 1] = '\0';
177 : TRACE_INFO_STRING("KEY:", *key);
178 :
179 280 : eq++;
180 280 : len--;
181 787 : while (isspace(*eq)) {
182 227 : eq++;
183 227 : len--;
184 : }
185 :
186 280 : *value = eq;
187 : /* Make sure we include trailing 0 into data */
188 280 : *length = len + 1;
189 :
190 : TRACE_INFO_STRING("VALUE:", *value);
191 : TRACE_INFO_NUMBER("LENGTH:", *length);
192 :
193 : TRACE_FLOW_STRING("read_line", "Exit");
194 280 : return RET_PAIR;
195 : }
196 :
197 : /************************************************************/
198 : /* REMOVE FUNCTION ABOVE */
199 : /************************************************************/
200 :
201 :
202 : /***************************************************************************/
203 : /* Function to read single ini file and pupulate
204 : * the provided collection with subcollcetions from the file */
205 11 : static int ini_to_collection(FILE *file,
206 : const char *config_filename,
207 : struct collection_item *ini_config,
208 : int error_level,
209 : struct collection_item **error_list,
210 : struct collection_item *lines)
211 : {
212 : int error;
213 : int status;
214 11 : int section_count = 0;
215 11 : char *key = NULL;
216 11 : char *value = NULL;
217 11 : struct collection_item *current_section = NULL;
218 : int length;
219 11 : int ext_err = -1;
220 : struct parse_error pe;
221 11 : int line = 0;
222 11 : int created = 0;
223 : char buf[BUFFER_SIZE+1];
224 :
225 :
226 : TRACE_FLOW_STRING("ini_to_collection", "Entry");
227 :
228 : /* Open the collection of errors */
229 11 : if (error_list != NULL) {
230 11 : *error_list = NULL;
231 11 : error = col_create_collection(error_list, INI_ERROR, COL_CLASS_INI_PERROR);
232 11 : if (error) {
233 : TRACE_ERROR_NUMBER("Failed to create error collection", error);
234 : return error;
235 : }
236 : /* Add file name as the first item */
237 11 : error = col_add_str_property(*error_list, NULL, INI_ERROR_NAME, config_filename, 0);
238 11 : if (error) {
239 : TRACE_ERROR_NUMBER("Failed to and name to collection", error);
240 0 : col_destroy_collection(*error_list);
241 : return error;
242 : }
243 : created = 1;
244 : }
245 :
246 : /* Read file lines */
247 : while (1) {
248 : /* Always read one less than the buffer */
249 651 : status = read_line(file, buf, BUFFER_SIZE+1, &key, &value, &length, &ext_err);
250 651 : if (status == RET_EOF) break;
251 :
252 640 : line++;
253 :
254 640 : switch (status) {
255 : case RET_PAIR:
256 :
257 : #ifdef HAVE_VALIDATION
258 :
259 : /* Add line to the collection of lines.
260 : * It is pretty safe in this case to just type cast the value to
261 : * int32_t since it is unrealistic that ini file will ever have
262 : * so many lines.
263 : */
264 : if (lines) {
265 : error = col_add_int_property(lines, NULL, key, (int32_t)line);
266 : if (error) {
267 : TRACE_ERROR_NUMBER("Failed to add line to line collection", error);
268 : col_destroy_collection(current_section);
269 : if (created) {
270 : col_destroy_collection(*error_list);
271 : *error_list = NULL;
272 : }
273 : return error;
274 : }
275 : }
276 :
277 : #endif /* HAVE_VALIDATION */
278 :
279 : /* Do we have a section at the top of the file ? */
280 280 : if (section_count == 0) {
281 : /* Check if collection already exists */
282 8 : error = col_get_collection_reference(ini_config, ¤t_section,
283 : INI_DEFAULT_SECTION);
284 8 : if (error != EOK) {
285 : /* Create default collection */
286 7 : if ((error = col_create_collection(¤t_section,
287 : INI_DEFAULT_SECTION,
288 7 : COL_CLASS_INI_SECTION)) ||
289 7 : (error = col_add_collection_to_collection(ini_config,
290 : NULL,NULL,
291 : current_section,
292 : COL_ADD_MODE_REFERENCE))) {
293 : TRACE_ERROR_NUMBER("Failed to create collection", error);
294 0 : col_destroy_collection(current_section);
295 0 : if (created) {
296 0 : col_destroy_collection(*error_list);
297 0 : *error_list = NULL;
298 : }
299 : return error;
300 : }
301 : }
302 8 : section_count++;
303 : }
304 :
305 : /* Put value into the collection */
306 280 : error = col_insert_str_property(current_section,
307 : NULL,
308 : COL_DSP_END,
309 : NULL,
310 : 0,
311 : COL_INSERT_DUPOVER,
312 : key,
313 : value,
314 : length);
315 280 : if (error != EOK) {
316 : TRACE_ERROR_NUMBER("Failed to add pair to collection", error);
317 0 : col_destroy_collection(current_section);
318 0 : if (created) {
319 0 : col_destroy_collection(*error_list);
320 0 : *error_list = NULL;
321 : }
322 : return error;
323 : }
324 : break;
325 :
326 : case RET_SECTION:
327 :
328 : #ifdef HAVE_VALIDATION
329 :
330 : /* Add line to the collection of lines */
331 : if (lines) {
332 : /* For easier search make line numbers for the sections negative.
333 : * This would allow differentiating sections and attributes.
334 : * It is pretty safe in this case to just type cast the value to
335 : * int32_t since it is unrealistic that ini file will ever have
336 : * so many lines.
337 : */
338 : error = col_add_int_property(lines, NULL, key, (int32_t)(-1 * line));
339 : if (error) {
340 : TRACE_ERROR_NUMBER("Failed to add line to line collection", error);
341 : col_destroy_collection(current_section);
342 : if (created) {
343 : col_destroy_collection(*error_list);
344 : *error_list = NULL;
345 : }
346 : return error;
347 : }
348 : }
349 :
350 : #endif /* HAVE_VALIDATION */
351 :
352 : /* Read a new section */
353 54 : col_destroy_collection(current_section);
354 54 : current_section = NULL;
355 :
356 54 : error = col_get_collection_reference(ini_config, ¤t_section, key);
357 54 : if (error != EOK) {
358 : /* Create default collection */
359 46 : if ((error = col_create_collection(¤t_section, key,
360 46 : COL_CLASS_INI_SECTION)) ||
361 46 : (error = col_add_collection_to_collection(ini_config,
362 : NULL, NULL,
363 : current_section,
364 : COL_ADD_MODE_REFERENCE))) {
365 : TRACE_ERROR_NUMBER("Failed to add collection", error);
366 0 : col_destroy_collection(current_section);
367 0 : if (created) {
368 0 : col_destroy_collection(*error_list);
369 0 : *error_list = NULL;
370 : }
371 : return error;
372 : }
373 : }
374 54 : section_count++;
375 : break;
376 :
377 : case RET_EMPTY:
378 : TRACE_INFO_STRING("Empty string", "");
379 : break;
380 :
381 : case RET_COMMENT:
382 : TRACE_INFO_STRING("Comment", "");
383 : break;
384 :
385 : case RET_ERROR:
386 : /* Try to add to the error list only if it is present */
387 21 : if (error_list) {
388 21 : pe.line = line;
389 21 : pe.error = ext_err;
390 21 : error = col_add_binary_property(*error_list, NULL,
391 21 : ERROR_TXT, &pe, sizeof(pe));
392 21 : if (error) {
393 : TRACE_ERROR_NUMBER("Failed to add error to collection",
394 : error);
395 0 : col_destroy_collection(current_section);
396 0 : if (created) {
397 0 : col_destroy_collection(*error_list);
398 0 : *error_list = NULL;
399 : }
400 : return error;
401 : }
402 : }
403 :
404 : /* Exit if there was an error parsing file */
405 21 : if (error_level != INI_STOP_ON_NONE) {
406 : TRACE_ERROR_STRING("Invalid format of the file", "");
407 0 : col_destroy_collection(current_section);
408 : return EIO;
409 : }
410 : break;
411 :
412 : case RET_INVALID:
413 : default:
414 : /* Try to add to the error list only if it is present */
415 53 : if (error_list) {
416 53 : pe.line = line;
417 53 : pe.error = ext_err;
418 53 : error = col_add_binary_property(*error_list, NULL,
419 53 : WARNING_TXT, &pe, sizeof(pe));
420 53 : if (error) {
421 : TRACE_ERROR_NUMBER("Failed to add warning to collection",
422 : error);
423 0 : col_destroy_collection(current_section);
424 0 : if (created) {
425 0 : col_destroy_collection(*error_list);
426 0 : *error_list = NULL;
427 : }
428 : return error;
429 : }
430 : }
431 :
432 : /* Exit if we are told to exit on warnings */
433 53 : if (error_level == INI_STOP_ON_ANY) {
434 : TRACE_ERROR_STRING("Invalid format of the file", "");
435 0 : if (created) col_destroy_collection(current_section);
436 : return EIO;
437 : }
438 : TRACE_ERROR_STRING("Invalid string", "");
439 : break;
440 : }
441 640 : ext_err = -1;
442 : }
443 :
444 : /* Note: File is not closed on this level any more.
445 : * It opened on the level above, checked and closed there.
446 : * It is not the responsibility of this function to close
447 : * file any more.
448 : */
449 :
450 : COL_DEBUG_COLLECTION(ini_config);
451 :
452 11 : col_destroy_collection(current_section);
453 :
454 : COL_DEBUG_COLLECTION(ini_config);
455 :
456 : TRACE_FLOW_STRING("ini_to_collection", "Success Exit");
457 :
458 : return EOK;
459 : }
460 :
461 : /*********************************************************************/
462 : /* Function to free configuration */
463 10 : void free_ini_config(struct collection_item *ini_config)
464 : {
465 : TRACE_FLOW_STRING("free_ini_config", "Entry");
466 10 : col_destroy_collection(ini_config);
467 : TRACE_FLOW_STRING("free_ini_config", "Exit");
468 10 : }
469 :
470 : /* Function to free configuration error list */
471 9 : void free_ini_config_errors(struct collection_item *error_set)
472 : {
473 : TRACE_FLOW_STRING("free_ini_config_errors", "Entry");
474 9 : col_destroy_collection(error_set);
475 : TRACE_FLOW_STRING("free_ini_config_errors", "Exit");
476 9 : }
477 :
478 : #ifdef HAVE_VALIDATION
479 :
480 : /* Function to free configuration lines list.
481 : *
482 : * The following doxygen description is moved here.
483 : * When the function gets exposed move it into
484 : * the header file.
485 : */
486 : /**
487 : * @brief Function to free lines object.
488 : *
489 : * EXPERIMENTAL. Reserved for future use.
490 : *
491 : * @param[in] lines Lines object.
492 : *
493 : */
494 :
495 : void free_ini_config_lines(struct collection_item *lines)
496 : {
497 : TRACE_FLOW_STRING("free_ini_config_lines", "Entry");
498 : col_destroy_collection(lines);
499 : TRACE_FLOW_STRING("free_ini_config_lines", "Exit");
500 : }
501 :
502 : #endif /* HAVE_VALIDATION */
503 :
504 :
505 : /* Read configuration information from a file */
506 2 : int config_from_file(const char *application,
507 : const char *config_filename,
508 : struct collection_item **ini_config,
509 : int error_level,
510 : struct collection_item **error_list)
511 : {
512 : int error;
513 :
514 : TRACE_FLOW_STRING("config_from_file", "Entry");
515 2 : error = config_from_file_with_metadata(application,
516 : config_filename,
517 : ini_config,
518 : error_level,
519 : error_list,
520 : 0,
521 : NULL);
522 : TRACE_FLOW_NUMBER("config_from_file. Returns", error);
523 2 : return error;
524 : }
525 :
526 : /* Read configuration information from a file descriptor */
527 1 : int config_from_fd(const char *application,
528 : int fd,
529 : const char *config_source,
530 : struct collection_item **ini_config,
531 : int error_level,
532 : struct collection_item **error_list)
533 : {
534 : int error;
535 :
536 : TRACE_FLOW_STRING("config_from_fd", "Entry");
537 1 : error = config_from_fd_with_metadata(application,
538 : fd,
539 : config_source,
540 : ini_config,
541 : error_level,
542 : error_list,
543 : 0,
544 : NULL);
545 : TRACE_FLOW_NUMBER("config_from_fd. Returns", error);
546 1 : return error;
547 : }
548 :
549 :
550 :
551 : /* Low level function that prepares the collection
552 : * and calls parser.
553 : */
554 11 : static int config_with_metadata(const char *application,
555 : FILE *config_file,
556 : const char *config_source,
557 : struct collection_item **ini_config,
558 : int error_level,
559 : struct collection_item **error_list,
560 : uint32_t metaflags,
561 : struct collection_item **metadata)
562 : {
563 : int error;
564 11 : int created = 0;
565 11 : struct collection_item *lines = NULL;
566 :
567 : #ifdef HAVE_VALIDATION
568 : int created_lines = 0;
569 : #endif
570 :
571 : TRACE_FLOW_STRING("config_from_file", "Entry");
572 :
573 : /* Now we check arguments in the calling functions. */
574 :
575 : /* Create collection if needed */
576 11 : if (*ini_config == NULL) {
577 5 : error = col_create_collection(ini_config,
578 : application,
579 : COL_CLASS_INI_CONFIG);
580 5 : if (error != EOK) {
581 : TRACE_ERROR_NUMBER("Failed to create collection", error);
582 : return error;
583 : }
584 : created = 1;
585 : }
586 : /* Is the collection of the right class? */
587 6 : else if (((col_is_of_class(*ini_config, COL_CLASS_INI_CONFIG))== 0) &&
588 0 : ((col_is_of_class(*ini_config, COL_CLASS_INI_META))== 0)) {
589 : TRACE_ERROR_NUMBER("Wrong collection type", EINVAL);
590 : return EINVAL;
591 : }
592 :
593 : #ifdef HAVE_VALIDATION
594 : /* This code is preserved for future use */
595 : error = col_create_collection(lines,
596 : application,
597 : COL_CLASS_INI_LINES);
598 : if (error != EOK) {
599 : TRACE_ERROR_NUMBER("Failed to create collection", error);
600 : if (created) {
601 : col_destroy_collection(*ini_config);
602 : *ini_config = NULL;
603 : }
604 : return error;
605 : }
606 : created_lines = 1;
607 : #else
608 : /* Until we implement validation do not read the lines. */
609 11 : lines = NULL;
610 : #endif /* HAVE_VALIDATION */
611 :
612 : /* Do the actual work - for now do not read lines.*/
613 11 : error = ini_to_collection(config_file, config_source,
614 : *ini_config, error_level,
615 : error_list, lines);
616 : /* In case of error when we created collection - delete it */
617 11 : if (error && created) {
618 0 : col_destroy_collection(*ini_config);
619 0 : *ini_config = NULL;
620 : }
621 :
622 : /* FIXME - put lines collection into the metadata */
623 :
624 : TRACE_FLOW_NUMBER("config_from_file. Returns", error);
625 : return error;
626 : }
627 :
628 : /* Function to read the ini file from fd
629 : * with meta data.
630 : */
631 19 : int config_from_fd_with_metadata(const char *application,
632 : int ext_fd,
633 : const char *config_filename,
634 : struct collection_item **ini_config,
635 : int error_level,
636 : struct collection_item **error_list,
637 : uint32_t metaflags,
638 : struct collection_item **metadata)
639 : {
640 19 : int error = EOK;
641 19 : int file_error = EOK;
642 19 : int save_error = 0;
643 19 : int fd = -1;
644 19 : FILE *config_file = NULL;
645 : char abs_name[PATH_MAX + 1];
646 : char buff[CONVERSION_BUFFER];
647 :
648 : TRACE_FLOW_STRING("config_from_fd_with_metadata", "Entry");
649 :
650 : /* We need to check arguments before we can move on,
651 : * and start allocating memory.
652 : */
653 38 : if ((ini_config == NULL) ||
654 19 : (application == NULL)) {
655 : TRACE_ERROR_NUMBER("Invalid argument", EINVAL);
656 : return EINVAL;
657 : }
658 :
659 : /* Prepare meta data */
660 19 : error = prepare_metadata(metaflags, metadata, &save_error);
661 19 : if (error) {
662 : TRACE_ERROR_NUMBER("Failed to prepare metadata", error);
663 : return error;
664 : }
665 :
666 19 : errno = 0;
667 :
668 19 : if (ext_fd == -1) {
669 : /* No file descriptor so use name */
670 15 : config_file = fopen(config_filename, "r");
671 : }
672 : else {
673 : /* Create a copy of the descriptor so that we can close it if needed */
674 4 : fd = dup(ext_fd);
675 4 : if (fd != -1) config_file = fdopen(fd, "r");
676 : }
677 19 : file_error = errno;
678 :
679 19 : if (save_error) {
680 : /* Record the result of the open file operation in metadata */
681 3 : snprintf(buff, CONVERSION_BUFFER, "%d", file_error);
682 3 : error = col_add_str_property(*metadata,
683 : INI_META_SEC_ERROR,
684 : INI_META_KEY_READ_ERROR,
685 : buff,
686 : 0);
687 3 : if (error) {
688 : /* Something is really wrong if we failed here */
689 : TRACE_ERROR_NUMBER("Failed to save file open error", error);
690 0 : if (config_file) fclose(config_file);
691 0 : return error;
692 : }
693 : }
694 :
695 19 : if(!config_file) {
696 : TRACE_ERROR_NUMBER("Failed to open file", file_error);
697 : return file_error;
698 : }
699 :
700 : /* Normalize path for reporting purposes */
701 15 : error = make_normalized_absolute_path(abs_name,
702 : PATH_MAX,
703 : config_filename);
704 15 : if(error) {
705 : TRACE_ERROR_NUMBER("Failed to resolve path", error);
706 0 : fclose(config_file);
707 0 : return error;
708 : }
709 :
710 :
711 15 : if (metadata) {
712 : /* Collect meta data before actually parsing the file */
713 5 : error = collect_metadata(metaflags,
714 : metadata,
715 : config_file,
716 : abs_name);
717 5 : if(error) {
718 : TRACE_ERROR_NUMBER("Failed to collect metadata", error);
719 0 : fclose(config_file);
720 0 : return error;
721 : }
722 : }
723 :
724 15 : if (!(metaflags & INI_META_ACTION_NOPARSE)) {
725 : /* Parse data if needed */
726 11 : error = config_with_metadata(application,
727 : config_file,
728 : abs_name,
729 : ini_config,
730 : error_level,
731 : error_list,
732 : metaflags,
733 : metadata);
734 : }
735 :
736 : /* We opened the file we close it */
737 15 : fclose(config_file);
738 :
739 : TRACE_FLOW_NUMBER("config_from_fd_with_metadata. Returns", error);
740 15 : return error;
741 : }
742 :
743 : /* Function to read the ini file with metadata
744 : * using file name.
745 : */
746 15 : int config_from_file_with_metadata(const char *application,
747 : const char *config_filename,
748 : struct collection_item **ini_config,
749 : int error_level,
750 : struct collection_item **error_list,
751 : uint32_t metaflags,
752 : struct collection_item **metadata)
753 : {
754 15 : int error = EOK;
755 : TRACE_FLOW_STRING("config_from_file_with_metadata", "Entry");
756 :
757 15 : error = config_from_fd_with_metadata(application,
758 : -1,
759 : config_filename,
760 : ini_config,
761 : error_level,
762 : error_list,
763 : metaflags,
764 : metadata);
765 :
766 : TRACE_FLOW_STRING("config_from_file_with_metadata", "Exit");
767 15 : return error;
768 : }
769 :
770 :
771 : /* Read default config file and then overwrite it with a specific one
772 : * from the directory */
773 10 : int config_for_app_with_metadata(const char *application,
774 : const char *config_file,
775 : const char *config_dir,
776 : struct collection_item **ini_config,
777 : int error_level,
778 : struct collection_item **error_set,
779 : uint32_t metaflags,
780 : struct collection_item **meta_default,
781 : struct collection_item **meta_appini)
782 : {
783 10 : int error = EOK;
784 : char *file_name;
785 10 : struct collection_item *error_list_common = NULL;
786 10 : struct collection_item *error_list_specific = NULL;
787 10 : struct collection_item **pass_common = NULL;
788 10 : struct collection_item **pass_specific = NULL;
789 10 : int created = 0;
790 10 : int tried = 0;
791 10 : int noents = 0;
792 :
793 : TRACE_FLOW_STRING("config_for_app", "Entry");
794 :
795 10 : if (ini_config == NULL) {
796 : TRACE_ERROR_NUMBER("Invalid parameter", EINVAL);
797 : return EINVAL;
798 : }
799 :
800 7 : if ((config_file == NULL) && (config_dir == NULL)) {
801 : TRACE_ERROR_NUMBER("Noop call of the function is invalid", EINVAL);
802 : return EINVAL;
803 : }
804 :
805 : /* Prepare error collection pointers */
806 6 : if (error_set != NULL) {
807 : TRACE_INFO_STRING("Error set is not NULL", "preparing error set");
808 5 : pass_common = &error_list_common;
809 5 : pass_specific = &error_list_specific;
810 5 : *error_set = NULL;
811 : /* Construct the overarching error collection */
812 5 : error = col_create_collection(error_set,
813 : FILE_ERROR_SET,
814 : COL_CLASS_INI_PESET);
815 5 : if (error != EOK) {
816 : TRACE_ERROR_NUMBER("Failed to create collection", error);
817 : return error;
818 : }
819 : }
820 : else {
821 : TRACE_INFO_STRING("No error set. Errors will not be captured", "");
822 : pass_common = NULL;
823 : pass_specific = NULL;
824 : }
825 :
826 : /* Create collection if needed */
827 6 : if (*ini_config == NULL) {
828 : TRACE_INFO_STRING("New config collection. Allocate.", "");
829 6 : error = col_create_collection(ini_config,
830 : application,
831 : COL_CLASS_INI_CONFIG);
832 6 : if (error != EOK) {
833 : TRACE_ERROR_NUMBER("Failed to create collection", error);
834 0 : if (error_set) {
835 0 : col_destroy_collection(*error_set);
836 0 : *error_set = NULL;
837 : }
838 0 : return error;
839 : }
840 : created = 1;
841 : }
842 : /* Is the collection of the right class? */
843 0 : else if ((col_is_of_class(*ini_config, COL_CLASS_INI_CONFIG) == 0) &&
844 0 : (col_is_of_class(*ini_config, COL_CLASS_INI_META) == 0)) {
845 : TRACE_ERROR_NUMBER("Wrong collection type", EINVAL);
846 : return EINVAL;
847 : }
848 :
849 : /* Read master file */
850 6 : if (config_file != NULL) {
851 : TRACE_INFO_STRING("Reading master file:", config_file);
852 : /* Get configuration information from the file */
853 4 : error = config_from_file_with_metadata(application,
854 : config_file,
855 : ini_config,
856 : error_level,
857 : pass_common,
858 : metaflags,
859 : meta_default);
860 4 : tried++;
861 : /* ENOENT and EOK are Ok */
862 4 : if (error) {
863 2 : if (error != ENOENT) {
864 : TRACE_ERROR_NUMBER("Failed to read master file", error);
865 : /* In case of error when we created collection - delete it */
866 0 : if(error && created) {
867 0 : col_destroy_collection(*ini_config);
868 0 : *ini_config = NULL;
869 : }
870 : /* We do not clear the error_set here */
871 0 : return error;
872 : }
873 : else noents++;
874 : }
875 : /* Add error results if any to the overarching error collection */
876 4 : if ((pass_common != NULL) && (*pass_common != NULL)) {
877 : TRACE_INFO_STRING("Process errors resulting from file:", config_file);
878 2 : error = col_add_collection_to_collection(*error_set, NULL, NULL,
879 : *pass_common,
880 : COL_ADD_MODE_EMBED);
881 2 : if (error) {
882 0 : if (created) {
883 0 : col_destroy_collection(*ini_config);
884 0 : *ini_config = NULL;
885 : }
886 0 : if (error_set) {
887 0 : col_destroy_collection(*error_set);
888 0 : *error_set = NULL;
889 : }
890 : TRACE_ERROR_NUMBER("Failed to add error collection to another error collection", error);
891 0 : return error;
892 : }
893 : }
894 : }
895 :
896 6 : if (config_dir != NULL) {
897 : /* Get specific application file */
898 5 : file_name = malloc(strlen(config_dir) + strlen(application) + NAME_OVERHEAD);
899 5 : if (file_name == NULL) {
900 0 : error = ENOMEM;
901 : TRACE_ERROR_NUMBER("Failed to allocate memory for file name", error);
902 : /* In case of error when we created collection - delete it */
903 0 : if(created) {
904 0 : col_destroy_collection(*ini_config);
905 0 : *ini_config = NULL;
906 : }
907 0 : if (error_set) {
908 0 : col_destroy_collection(*error_set);
909 0 : *error_set = NULL;
910 : }
911 : return error;
912 : }
913 :
914 : /* It is safe to use sprintf() here */
915 5 : sprintf(file_name, "%s%s%s.conf", config_dir, SLASH, application);
916 : TRACE_INFO_STRING("Opening file:", file_name);
917 : /* Read specific file */
918 5 : error = config_from_file_with_metadata(application,
919 : file_name,
920 : ini_config,
921 : error_level,
922 : pass_specific,
923 : metaflags,
924 : meta_appini);
925 5 : tried++;
926 5 : free(file_name);
927 : /* ENOENT and EOK are Ok */
928 5 : if (error) {
929 1 : if (error != ENOENT) {
930 : TRACE_ERROR_NUMBER("Failed to read specific application file", error);
931 : /* In case of error when we created collection - delete it */
932 0 : if (error && created) {
933 0 : col_destroy_collection(*ini_config);
934 0 : *ini_config = NULL;
935 : }
936 : /* We do not clear the error_set here */
937 0 : return error;
938 : }
939 1 : else noents++;
940 : }
941 : /* Add error results if any to the overarching error collection */
942 5 : if ((pass_specific != NULL) && (*pass_specific != NULL)) {
943 4 : error = col_add_collection_to_collection(*error_set, NULL, NULL,
944 : *pass_specific,
945 : COL_ADD_MODE_EMBED);
946 4 : if (error) {
947 0 : if (created) {
948 0 : col_destroy_collection(*ini_config);
949 0 : *ini_config = NULL;
950 : }
951 0 : if (error_set) {
952 0 : col_destroy_collection(*error_set);
953 0 : *error_set = NULL;
954 : }
955 : TRACE_ERROR_NUMBER("Failed to add error collection to another error collection", error);
956 0 : return error;
957 : }
958 : }
959 : }
960 :
961 : /* If we failed to read or access file as many
962 : * times as we tried and we told to stop on any errors
963 : * we should report an error.
964 : */
965 : TRACE_INFO_NUMBER("Tried:", tried);
966 : TRACE_INFO_NUMBER("Noents:", noents);
967 :
968 6 : if ((tried == noents) && (error_level == INI_STOP_ON_ANY)) {
969 : TRACE_ERROR_NUMBER("Fail to read or access all the files tried", ENOENT);
970 1 : if (created) {
971 1 : col_destroy_collection(*ini_config);
972 1 : *ini_config = NULL;
973 : }
974 1 : if (error_set) {
975 1 : col_destroy_collection(*error_set);
976 1 : *error_set = NULL;
977 : }
978 : return ENOENT;
979 : }
980 :
981 : TRACE_FLOW_STRING("config_to_collection", "Exit");
982 : return EOK;
983 : }
984 :
985 :
986 : /* Function to return configuration data
987 : * for the application without meta data.
988 : */
989 10 : int config_for_app(const char *application,
990 : const char *config_file,
991 : const char *config_dir,
992 : struct collection_item **ini_config,
993 : int error_level,
994 : struct collection_item **error_set)
995 : {
996 10 : int error = EOK;
997 : TRACE_FLOW_STRING("config_for_app", "Entry");
998 :
999 10 : error = config_for_app_with_metadata(application,
1000 : config_file,
1001 : config_dir,
1002 : ini_config,
1003 : error_level,
1004 : error_set,
1005 : 0,
1006 : NULL,
1007 : NULL);
1008 :
1009 : TRACE_FLOW_NUMBER("config_for_app. Returning", error);
1010 10 : return error;
1011 : }
|