blob: 4972cf5d5cdde33c7f371194ad69c7e85469cd63 [file] [log] [blame]
Zi Lin5158f552021-10-27 00:55:52 +00001/* Copyright 2021 The Chromium OS Authors. All rights reserved.
2 * Use of this source code is governed by a BSD-style license that can be
3 * found in the LICENSE file.
4 */
5
6#include <errno.h>
7#include <stdbool.h>
8#include <stdio.h>
9#include <stdlib.h>
10#include <string.h>
11
12#include "config_parser.h"
13
14#include "util.h"
15
16#define LIST_DEFAULT_SIZE (100)
17
18struct config_entry_list *new_config_entry_list(void)
19{
20 /*
21 * There are <100 CLI options, configuration file will likely have
22 * a similar number of config entries.
23 */
24 struct config_entry *entries =
25 calloc(LIST_DEFAULT_SIZE, sizeof(struct config_entry));
26 if (!entries)
27 return NULL;
28
29 struct config_entry_list *list =
30 calloc(1, sizeof(struct config_entry_list));
31 if (!list) {
32 free(entries);
33 return NULL;
34 }
35 list->entries = entries;
36 list->num_allocated_ = LIST_DEFAULT_SIZE;
37 list->num_entries = 0;
38 return list;
39}
40
41void clear_config_entry(struct config_entry *entry)
42{
43 free((char *)entry->key);
44 free((char *)entry->value);
45}
46
47void free_config_entry_list(struct config_entry_list *list)
48{
49 if (!list)
50 return;
51 for (size_t i = 0; i < list->num_entries; i++) {
52 clear_config_entry(&list->entries[i]);
53 }
54 free(list->entries);
55 free(list);
56}
57
58bool parse_config_line(const char *config_line, struct config_entry *entry)
59{
60 /* Parsing will modify |config_line| in place, so make a copy. */
61 attribute_cleanup_str char *line = strdup(config_line);
62 if (!line)
63 return false;
64 char *value = line;
65
66 /* After tokenize call, |value| will point to a substring after '='.
Zi Lin44461c72021-11-16 18:37:27 +000067 * If there is no '=' in the string, |key| will contain the entire
68 * string while |value| will be NULL.
Zi Lin5158f552021-10-27 00:55:52 +000069 */
70 char *key = tokenize(&value, "=");
71 if (key)
72 key = strip(key);
73 if (value)
74 value = strip(value);
Zi Lin44461c72021-11-16 18:37:27 +000075 if (!key || key[0] == '\0' || (value && value[0] == '\0')) {
Zi Lin5158f552021-10-27 00:55:52 +000076 warn("unable to parse %s", config_line);
77 return false;
78 }
79 entry->key = strdup(key);
Zi Lin44461c72021-11-16 18:37:27 +000080 entry->value = value ? strdup(value) : NULL;
81 if (!entry->key || (value && !entry->value)) {
Zi Lin5158f552021-10-27 00:55:52 +000082 clear_config_entry(entry);
83 return false;
84 }
85 return true;
86}
87
88static bool match_special_directive(const char *line)
89{
90 return (strcmp(line, "% minijail-config-file v0\n") == 0);
91}
92
93bool parse_config_file(FILE *config_file, struct config_entry_list *list)
94{
95 attribute_cleanup_str char *line = NULL;
96 size_t len = 0;
97
98 /* The first line must match the special directive */
99 if (getline(&line, &len, config_file) == -1 ||
100 !match_special_directive(line))
101 return false;
102 while (getmultiline(&line, &len, config_file) != -1) {
103 char *stripped_line = strip(line);
104 /*
105 * Skip blank lines and all comments. Comment lines start with
106 * '#'.
107 */
108 if (stripped_line[0] == '\0' || stripped_line[0] == '#')
109 continue;
110
111 /*
112 * Check if the list is full, and reallocate with doubled
113 * capacity if so.
114 */
115 if (list->num_entries >= list->num_allocated_) {
116 list->num_allocated_ = list->num_allocated_ * 2;
117 list->entries = realloc(
118 list->entries,
119 list->num_allocated_ * sizeof(struct config_entry));
120 if (list->entries == NULL) {
121 return false;
122 }
123 }
124
125 struct config_entry *entry = &list->entries[list->num_entries];
126 if (!parse_config_line(stripped_line, entry)) {
127 return false;
128 }
129 ++list->num_entries;
130 }
131 /*
132 * getmultiline() behaves similarly with getline(3). It returns -1
133 * when read into EOF or the following errors.
134 */
135 if (errno == EINVAL || errno == ENOMEM) {
136 return false;
137 }
138
139 /* Shrink the list to save memory. */
140 if (list->num_entries < list->num_allocated_) {
141 list->entries =
142 realloc(list->entries,
143 list->num_entries * sizeof(struct config_entry));
144 list->num_allocated_ = list->num_entries;
145 }
146
147 return true;
148}