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