1 /**
2 * Copyright © 2016 IBM Corporation
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include <ctype.h>
18 #include <err.h>
19 #include <errno.h>
20 #include <fcntl.h>
21 #include <limits.h>
22 #include <stdint.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <strings.h>
27 #include <termios.h> /* for speed_t */
28 #include <unistd.h>
29
30 #include <sys/mman.h>
31 #include <sys/stat.h>
32
33 #include <iniparser/iniparser.h>
34
35 #include "config-internal.h"
36 #include "config.h"
37 #include "util.h"
38
39 static const char *config_default_filename = SYSCONFDIR "/obmc-console.conf";
40
config_get_value(struct config * config,const char * name)41 const char *config_get_value(struct config *config, const char *name)
42 {
43 char buf[CONFIG_MAX_KEY_LENGTH];
44 int rc;
45
46 if (!config->dict) {
47 return NULL;
48 }
49
50 rc = snprintf(buf, CONFIG_MAX_KEY_LENGTH, ":%s", name);
51 if (rc < 0) {
52 return NULL;
53 }
54
55 if ((size_t)rc >= sizeof(buf)) {
56 return NULL;
57 }
58
59 const char *value = iniparser_getstring(config->dict, buf, NULL);
60 if (value && strlen(value) == 0) {
61 return NULL;
62 }
63
64 return value;
65 }
66
config_init(const char * filename)67 struct config *config_init(const char *filename)
68 {
69 struct config *config;
70 dictionary *dict;
71
72 if (!filename) {
73 filename = config_default_filename;
74 }
75
76 if (access(filename, R_OK) == 0) {
77 dict = iniparser_load(filename);
78 if (!dict) {
79 /* Assume this is a parse failure */
80 return NULL;
81 }
82 } else {
83 /* If a config file was explicitly specified, then lack of access is always an error */
84 if (filename != config_default_filename) {
85 warn("Failed to open configuration file at '%s'",
86 filename);
87 return NULL;
88 }
89
90 /* For the default config path, any result other than not-present is an error */
91 if (errno != ENOENT && errno != ENOTDIR) {
92 warn("Failed to open configuration file at '%s'",
93 filename);
94 return NULL;
95 }
96
97 /* Config not present at default path, pretend its empty */
98 dict = NULL;
99 }
100
101 config = malloc(sizeof(*config));
102 if (!config) {
103 iniparser_freedict(dict);
104 return NULL;
105 }
106
107 config->dict = dict;
108
109 return config;
110 }
111
config_get_section_value(struct config * config,const char * secname,const char * name)112 const char *config_get_section_value(struct config *config, const char *secname,
113 const char *name)
114 {
115 char buf[CONFIG_MAX_KEY_LENGTH];
116 int rc;
117
118 rc = snprintf(buf, sizeof(buf), "%s:%s", secname, name);
119 if (rc < 0) {
120 return NULL;
121 }
122
123 if ((size_t)rc >= sizeof(buf)) {
124 // error / key too long for the buffer
125 warnx("config: section:key too long for buffer: '%s':'%s'",
126 secname, name);
127 return NULL;
128 }
129
130 return iniparser_getstring(config->dict, buf, NULL);
131 }
132
config_fini(struct config * config)133 void config_fini(struct config *config)
134 {
135 if (!config) {
136 return;
137 }
138
139 if (config->dict) {
140 iniparser_freedict(config->dict);
141 }
142
143 free(config);
144 }
145
146 struct terminal_speed_name {
147 speed_t speed;
148 uint32_t baud;
149 const char *name;
150 };
151
152 #define TERM_SPEED(x) { B##x, x, #x }
153
154 // clang-format off
155 static const struct terminal_speed_name terminal_speeds[] = {
156 TERM_SPEED(50),
157 TERM_SPEED(75),
158 TERM_SPEED(110),
159 TERM_SPEED(134),
160 TERM_SPEED(150),
161 TERM_SPEED(200),
162 TERM_SPEED(300),
163 TERM_SPEED(600),
164 TERM_SPEED(1200),
165 TERM_SPEED(1800),
166 TERM_SPEED(2400),
167 TERM_SPEED(4800),
168 TERM_SPEED(9600),
169 TERM_SPEED(19200),
170 TERM_SPEED(38400),
171 TERM_SPEED(57600),
172 TERM_SPEED(115200),
173 TERM_SPEED(230400),
174 TERM_SPEED(460800),
175 TERM_SPEED(500000),
176 TERM_SPEED(576000),
177 TERM_SPEED(921600),
178 TERM_SPEED(1000000),
179 TERM_SPEED(1152000),
180 TERM_SPEED(1500000),
181 TERM_SPEED(2000000),
182 TERM_SPEED(2500000),
183 TERM_SPEED(3000000),
184 TERM_SPEED(3500000),
185 TERM_SPEED(4000000),
186 };
187 // clang-format on
188
config_parse_baud(speed_t * speed,const char * baud_string)189 int config_parse_baud(speed_t *speed, const char *baud_string)
190 {
191 size_t i;
192
193 for (i = 0; i < ARRAY_SIZE(terminal_speeds); i++) {
194 if (strcmp(baud_string, terminal_speeds[i].name) == 0) {
195 *speed = terminal_speeds[i].speed;
196 return 0;
197 }
198 }
199 return -1;
200 }
201
parse_baud_to_int(speed_t speed)202 uint32_t parse_baud_to_int(speed_t speed)
203 {
204 size_t i;
205
206 for (i = 0; i < ARRAY_SIZE(terminal_speeds); i++) {
207 if (terminal_speeds[i].speed == speed) {
208 return terminal_speeds[i].baud;
209 }
210 }
211 return 0;
212 }
213
parse_int_to_baud(uint32_t baud)214 speed_t parse_int_to_baud(uint32_t baud)
215 {
216 size_t i;
217
218 for (i = 0; i < ARRAY_SIZE(terminal_speeds); i++) {
219 if (terminal_speeds[i].baud == baud) {
220 return terminal_speeds[i].speed;
221 }
222 }
223 return 0;
224 }
225
config_parse_bytesize(const char * size_str,size_t * size)226 int config_parse_bytesize(const char *size_str, size_t *size)
227 {
228 struct size_suffix_shift {
229 /* Left shiftwidth corresponding to the suffix. */
230 size_t shiftwidth;
231 int unit;
232 };
233
234 const struct size_suffix_shift suffixes[] = {
235 { 10, 'k' },
236 { 20, 'M' },
237 { 30, 'G' },
238 };
239 const size_t num_suffixes =
240 sizeof(suffixes) / sizeof(struct size_suffix_shift);
241 size_t logsize;
242 char *suffix;
243 size_t i;
244
245 if (!size_str) {
246 return -1;
247 }
248
249 logsize = strtoul(size_str, &suffix, 0);
250 if (logsize == 0 || logsize >= UINT32_MAX || suffix == size_str) {
251 return -1;
252 }
253
254 /* Ignore spaces between number and suffix */
255 while (*suffix && isspace(*suffix)) {
256 suffix++;
257 }
258
259 for (i = 0; i < num_suffixes; i++) {
260 if (*suffix == suffixes[i].unit) {
261 /*
262 * If logsize overflows, probably something was wrong.
263 * Return instead of clamping to an arbitrary value.
264 */
265 if (logsize > (UINT32_MAX >> suffixes[i].shiftwidth)) {
266 return -1;
267 }
268
269 logsize <<= suffixes[i].shiftwidth;
270 suffix++;
271 break;
272 }
273 }
274
275 /* Allow suffix like 'kB' */
276 while (*suffix && (tolower(*suffix) == 'b' || isspace(*suffix))) {
277 suffix++;
278 }
279
280 if (*suffix) {
281 warn("Invalid suffix!");
282 return -1;
283 }
284
285 *size = logsize;
286 return 0;
287 }
288
289 /* Default console id if not specified on command line or in config */
290 #define DEFAULT_CONSOLE_ID "default"
291
292 /* Get the console id */
config_resolve_console_id(struct config * config,const char * id_arg)293 const char *config_resolve_console_id(struct config *config, const char *id_arg)
294 {
295 const char *configured;
296
297 if (id_arg) {
298 return id_arg;
299 }
300
301 if ((configured = config_get_value(config, "console-id"))) {
302 return configured;
303 }
304
305 return DEFAULT_CONSOLE_ID;
306 }
307
config_count_sections(struct config * config)308 int config_count_sections(struct config *config)
309 {
310 return iniparser_getnsec(config->dict);
311 }
312
config_get_section_name(struct config * config,int i)313 const char *config_get_section_name(struct config *config, int i)
314 {
315 return iniparser_getsecname(config->dict, i);
316 }
317