xref: /openbmc/obmc-console/config.c (revision b965c220)
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