xref: /openbmc/obmc-console/config.c (revision d6e8b64a)
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 #include "console-server.h"
33 
34 static const char *config_default_filename = SYSCONFDIR "/obmc-console.conf";
35 
36 struct config_item {
37 	char *name;
38 	char *value;
39 	struct config_item *next;
40 };
41 
42 struct config {
43 	struct config_item *items;
44 };
45 
config_get_value(struct config * config,const char * name)46 const char *config_get_value(struct config *config, const char *name)
47 {
48 	struct config_item *item;
49 
50 	if (!config) {
51 		return NULL;
52 	}
53 
54 	for (item = config->items; item; item = item->next) {
55 		if (!strcasecmp(item->name, name)) {
56 			return item->value;
57 		}
58 	}
59 
60 	return NULL;
61 }
62 
config_parse(struct config * config,char * buf)63 static void config_parse(struct config *config, char *buf)
64 {
65 	struct config_item *item;
66 	char *name;
67 	char *value;
68 	char *p;
69 	char *line;
70 
71 	for (p = NULL, line = strtok_r(buf, "\n", &p); line;
72 	     line = strtok_r(NULL, "\n", &p)) {
73 		char *end;
74 		int rc;
75 
76 		/* trim leading space */
77 		while (isspace(*line)) {
78 			line++;
79 		}
80 
81 		/* skip comments */
82 		if (*line == '#') {
83 			continue;
84 		}
85 
86 		name = malloc(strlen(line));
87 		value = malloc(strlen(line));
88 		if (name && value) {
89 			rc = sscanf(line, "%[^ =] = %[^#]s", name, value);
90 		} else {
91 			rc = -ENOMEM;
92 		}
93 
94 		if (rc != 2) {
95 			free(name);
96 			free(value);
97 			continue;
98 		}
99 
100 		/* trim trailing space */
101 		end = value + strlen(value) - 1;
102 		while (isspace(*end)) {
103 			*end-- = '\0';
104 		}
105 
106 		/* create a new item and add to our list */
107 		item = malloc(sizeof(*item));
108 		item->name = name;
109 		item->value = value;
110 		item->next = config->items;
111 		config->items = item;
112 	}
113 }
114 
config_init_fd(int fd,const char * filename)115 static struct config *config_init_fd(int fd, const char *filename)
116 {
117 	struct config *config;
118 	size_t size;
119 	size_t len;
120 	ssize_t rc;
121 	char *buf;
122 
123 	size = 4096;
124 	len = 0;
125 	buf = malloc(size + 1);
126 	config = NULL;
127 
128 	for (;;) {
129 		rc = read(fd, buf + len, size - len);
130 		if (rc < 0) {
131 			warn("Can't read from configuration file %s", filename);
132 			goto out_free;
133 
134 		} else if (!rc) {
135 			break;
136 		}
137 		len += rc;
138 		if (len == size) {
139 			size <<= 1;
140 			buf = realloc(buf, size + 1);
141 		}
142 	}
143 	buf[len] = '\0';
144 
145 	config = malloc(sizeof(*config));
146 	config->items = NULL;
147 
148 	config_parse(config, buf);
149 
150 out_free:
151 	free(buf);
152 	return config;
153 }
154 
config_init(const char * filename)155 struct config *config_init(const char *filename)
156 {
157 	struct config *config;
158 	int fd;
159 
160 	if (!filename) {
161 		filename = config_default_filename;
162 	}
163 
164 	fd = open(filename, O_RDONLY);
165 	if (fd < 0) {
166 		warn("Can't open configuration file %s", filename);
167 		return NULL;
168 	}
169 
170 	config = config_init_fd(fd, filename);
171 
172 	close(fd);
173 
174 	return config;
175 }
176 
config_fini(struct config * config)177 void config_fini(struct config *config)
178 {
179 	struct config_item *item;
180 	struct config_item *next;
181 
182 	if (!config) {
183 		return;
184 	}
185 
186 	for (item = config->items; item; item = next) {
187 		next = item->next;
188 		free(item->name);
189 		free(item->value);
190 		free(item);
191 	}
192 
193 	free(config);
194 }
195 
196 struct terminal_speed_name {
197 	speed_t speed;
198 	uint32_t baud;
199 	const char *name;
200 };
201 
202 #define TERM_SPEED(x)                                                          \
203 	{                                                                      \
204 		B##x, x, #x                                                    \
205 	}
206 
207 // clang-format off
208 static const struct terminal_speed_name terminal_speeds[] = {
209 	TERM_SPEED(50),
210 	TERM_SPEED(75),
211 	TERM_SPEED(110),
212 	TERM_SPEED(134),
213 	TERM_SPEED(150),
214 	TERM_SPEED(200),
215 	TERM_SPEED(300),
216 	TERM_SPEED(600),
217 	TERM_SPEED(1200),
218 	TERM_SPEED(1800),
219 	TERM_SPEED(2400),
220 	TERM_SPEED(4800),
221 	TERM_SPEED(9600),
222 	TERM_SPEED(19200),
223 	TERM_SPEED(38400),
224 	TERM_SPEED(57600),
225 	TERM_SPEED(115200),
226 	TERM_SPEED(230400),
227 	TERM_SPEED(460800),
228 	TERM_SPEED(500000),
229 	TERM_SPEED(576000),
230 	TERM_SPEED(921600),
231 	TERM_SPEED(1000000),
232 	TERM_SPEED(1152000),
233 	TERM_SPEED(1500000),
234 	TERM_SPEED(2000000),
235 	TERM_SPEED(2500000),
236 	TERM_SPEED(3000000),
237 	TERM_SPEED(3500000),
238 	TERM_SPEED(4000000),
239 };
240 // clang-format on
241 
config_parse_baud(speed_t * speed,const char * baud_string)242 int config_parse_baud(speed_t *speed, const char *baud_string)
243 {
244 	size_t i;
245 
246 	for (i = 0; i < ARRAY_SIZE(terminal_speeds); i++) {
247 		if (strcmp(baud_string, terminal_speeds[i].name) == 0) {
248 			*speed = terminal_speeds[i].speed;
249 			return 0;
250 		}
251 	}
252 	return -1;
253 }
254 
parse_baud_to_int(speed_t speed)255 uint32_t parse_baud_to_int(speed_t speed)
256 {
257 	size_t i;
258 
259 	for (i = 0; i < ARRAY_SIZE(terminal_speeds); i++) {
260 		if (terminal_speeds[i].speed == speed) {
261 			return terminal_speeds[i].baud;
262 		}
263 	}
264 	return 0;
265 }
266 
parse_int_to_baud(uint32_t baud)267 speed_t parse_int_to_baud(uint32_t baud)
268 {
269 	size_t i;
270 
271 	for (i = 0; i < ARRAY_SIZE(terminal_speeds); i++) {
272 		if (terminal_speeds[i].baud == baud) {
273 			return terminal_speeds[i].speed;
274 		}
275 	}
276 	return 0;
277 }
278 
config_parse_bytesize(const char * size_str,size_t * size)279 int config_parse_bytesize(const char *size_str, size_t *size)
280 {
281 	struct size_suffix_shift {
282 		/* Left shiftwidth corresponding to the suffix. */
283 		size_t shiftwidth;
284 		int unit;
285 	};
286 
287 	const struct size_suffix_shift suffixes[] = {
288 		{ 10, 'k' },
289 		{ 20, 'M' },
290 		{ 30, 'G' },
291 	};
292 	const size_t num_suffixes =
293 		sizeof(suffixes) / sizeof(struct size_suffix_shift);
294 	size_t logsize;
295 	char *suffix;
296 	size_t i;
297 
298 	if (!size_str) {
299 		return -1;
300 	}
301 
302 	logsize = strtoul(size_str, &suffix, 0);
303 	if (logsize == 0 || logsize >= UINT32_MAX || suffix == size_str) {
304 		return -1;
305 	}
306 
307 	/* Ignore spaces between number and suffix */
308 	while (*suffix && isspace(*suffix)) {
309 		suffix++;
310 	}
311 
312 	for (i = 0; i < num_suffixes; i++) {
313 		if (*suffix == suffixes[i].unit) {
314 			/*
315 			 * If logsize overflows, probably something was wrong.
316 			 * Return instead of clamping to an arbitrary value.
317 			 */
318 			if (logsize > (UINT32_MAX >> suffixes[i].shiftwidth)) {
319 				return -1;
320 			}
321 
322 			logsize <<= suffixes[i].shiftwidth;
323 			suffix++;
324 			break;
325 		}
326 	}
327 
328 	/* Allow suffix like 'kB' */
329 	while (*suffix && (tolower(*suffix) == 'b' || isspace(*suffix))) {
330 		suffix++;
331 	}
332 
333 	if (*suffix) {
334 		warn("Invalid suffix!");
335 		return -1;
336 	}
337 
338 	*size = logsize;
339 	return 0;
340 }
341 
342 /* Default console id if not specified on command line or in config */
343 #define DEFAULT_CONSOLE_ID "default"
344 
345 /* Get the console id */
config_resolve_console_id(struct config * config,const char * id_arg)346 const char *config_resolve_console_id(struct config *config, const char *id_arg)
347 {
348 	const char *configured;
349 
350 	if (id_arg) {
351 		return id_arg;
352 	}
353 
354 	if ((configured = config_get_value(config, "console-id"))) {
355 		return configured;
356 	}
357 
358 	return DEFAULT_CONSOLE_ID;
359 }
360 
361 #ifdef CONFIG_TEST
main(void)362 int main(void)
363 {
364 	struct config_item *item;
365 	struct config *config;
366 
367 	config = config_init_fd(STDIN_FILENO, "<stdin>");
368 	if (!config)
369 		return EXIT_FAILURE;
370 
371 	for (item = config->items; item; item = item->next)
372 		printf("%s: %s\n", item->name, item->value);
373 
374 	config_fini(config);
375 
376 	return EXIT_SUCCESS;
377 }
378 #endif
379