blob: 392b4a13a4fa7be49efe2d178ba41ad47bf38c1a [file] [log] [blame]
Jeremy Kerrd831f962016-01-29 17:18:01 +08001/**
2 * Console server process for OpenBMC
3 *
4 * Copyright © 2016 IBM Corporation <jk@ozlabs.org>
5 */
6
Jeremy Kerr17217842016-01-29 18:44:21 +08007#define _GNU_SOURCE
8
Jeremy Kerr329a35f2016-03-10 15:36:01 +08009#include <assert.h>
Jeremy Kerr769cee12016-03-15 17:53:56 +080010#include <errno.h>
11#include <signal.h>
Jeremy Kerrd831f962016-01-29 17:18:01 +080012#include <stdint.h>
13#include <stdbool.h>
14#include <stdlib.h>
15#include <stdio.h>
16#include <fcntl.h>
17#include <unistd.h>
18#include <err.h>
Jeremy Kerrd831f962016-01-29 17:18:01 +080019#include <string.h>
20#include <getopt.h>
Jeremy Kerr17217842016-01-29 18:44:21 +080021#include <limits.h>
Jeremy Kerrd831f962016-01-29 17:18:01 +080022
23#include <sys/types.h>
24#include <sys/poll.h>
25
Jeremy Kerr1a0e03b2016-03-08 17:57:11 +080026#include "console-server.h"
Jeremy Kerrd831f962016-01-29 17:18:01 +080027
Jeremy Kerr1a0e03b2016-03-08 17:57:11 +080028struct console {
Jeremy Kerr17217842016-01-29 18:44:21 +080029 const char *tty_kname;
30 char *tty_sysfs_devnode;
31 char *tty_dev;
Jeremy Kerr957818b2016-03-08 14:35:15 +080032 int tty_sirq;
33 int tty_lpc_addr;
Jeremy Kerrd831f962016-01-29 17:18:01 +080034 int tty_fd;
Jeremy Kerr329a35f2016-03-10 15:36:01 +080035
Jeremy Kerr1a0e03b2016-03-08 17:57:11 +080036 struct handler **handlers;
37 int n_handlers;
Jeremy Kerr329a35f2016-03-10 15:36:01 +080038
39 struct poller **pollers;
40 int n_pollers;
41
42 struct pollfd *pollfds;
Jeremy Kerrd831f962016-01-29 17:18:01 +080043};
44
Jeremy Kerr329a35f2016-03-10 15:36:01 +080045struct poller {
46 struct handler *handler;
47 void *data;
48 poller_fn_t fn;
49 bool remove;
50};
51
52/* we have one extra entry in the pollfds array for the VUART tty */
53static const int n_internal_pollfds = 1;
54
Jeremy Kerr769cee12016-03-15 17:53:56 +080055/* state shared with the signal handler */
56static bool sigint;
Jeremy Kerr329a35f2016-03-10 15:36:01 +080057
Jeremy Kerrd831f962016-01-29 17:18:01 +080058static void usage(const char *progname)
59{
60 fprintf(stderr,
61"usage: %s [options]\n"
62"\n"
63"Options:\n"
Jeremy Kerrd66195c2016-03-16 17:24:51 +080064" --config <FILE> Use FILE for configuration\n"
Jeremy Kerrd831f962016-01-29 17:18:01 +080065"",
66 progname);
67}
68
Jeremy Kerr17217842016-01-29 18:44:21 +080069/* populates tty_dev and tty_sysfs_devnode, using the tty kernel name */
Jeremy Kerr1a0e03b2016-03-08 17:57:11 +080070static int tty_find_device(struct console *console)
Jeremy Kerr17217842016-01-29 18:44:21 +080071{
72 char *tty_class_device_link;
73 char *tty_device_tty_dir;
74 char *tty_device_reldir;
75 int rc;
76
77 rc = -1;
78 tty_class_device_link = NULL;
79 tty_device_tty_dir = NULL;
80 tty_device_reldir = NULL;
81
82 rc = asprintf(&tty_class_device_link,
Jeremy Kerr1a0e03b2016-03-08 17:57:11 +080083 "/sys/class/tty/%s", console->tty_kname);
Jeremy Kerr17217842016-01-29 18:44:21 +080084 if (rc < 0)
85 return -1;
86
87 tty_device_tty_dir = realpath(tty_class_device_link, NULL);
88 if (rc < 0) {
Jeremy Kerr1a0e03b2016-03-08 17:57:11 +080089 warn("Can't query sysfs for device %s", console->tty_kname);
Jeremy Kerr17217842016-01-29 18:44:21 +080090 goto out_free;
91 }
92
93 rc = asprintf(&tty_device_reldir, "%s/../../", tty_device_tty_dir);
94 if (rc < 0)
95 goto out_free;
96
Jeremy Kerr1a0e03b2016-03-08 17:57:11 +080097 console->tty_sysfs_devnode = realpath(tty_device_reldir, NULL);
98 if (!console->tty_sysfs_devnode)
99 warn("Can't find parent device for %s", console->tty_kname);
Jeremy Kerr17217842016-01-29 18:44:21 +0800100
101
102 /* todo: lookup from major/minor info in sysfs, in case udev has
103 * renamed us */
Jeremy Kerr1a0e03b2016-03-08 17:57:11 +0800104 rc = asprintf(&console->tty_dev, "/dev/%s", console->tty_kname);
Jeremy Kerr17217842016-01-29 18:44:21 +0800105 if (rc < 0)
106 goto out_free;
107
108 rc = 0;
109
110out_free:
111 free(tty_class_device_link);
112 free(tty_device_tty_dir);
113 free(tty_device_reldir);
114 return rc;
115}
116
Jeremy Kerr1a0e03b2016-03-08 17:57:11 +0800117static int tty_set_sysfs_attr(struct console *console, const char *name,
Jeremy Kerr957818b2016-03-08 14:35:15 +0800118 int value)
119{
120 char *path;
121 FILE *fp;
122 int rc;
123
Jeremy Kerr1a0e03b2016-03-08 17:57:11 +0800124 rc = asprintf(&path, "%s/%s", console->tty_sysfs_devnode, name);
Jeremy Kerr957818b2016-03-08 14:35:15 +0800125 if (rc < 0)
126 return -1;
127
128 fp = fopen(path, "w");
129 if (!fp) {
130 warn("Can't access attribute %s on device %s",
Jeremy Kerr1a0e03b2016-03-08 17:57:11 +0800131 name, console->tty_kname);
Jeremy Kerr957818b2016-03-08 14:35:15 +0800132 rc = -1;
133 goto out_free;
134 }
135 setvbuf(fp, NULL, _IONBF, 0);
136
137 rc = fprintf(fp, "0x%x", value);
138 if (rc < 0)
139 warn("Error writing to %s attribute of device %s",
Jeremy Kerr1a0e03b2016-03-08 17:57:11 +0800140 name, console->tty_kname);
Jeremy Kerr957818b2016-03-08 14:35:15 +0800141 fclose(fp);
142
143
144
145out_free:
146 free(path);
147 return rc;
148}
149
Jeremy Kerrd831f962016-01-29 17:18:01 +0800150/**
151 * Open and initialise the serial device
152 */
Jeremy Kerr1a0e03b2016-03-08 17:57:11 +0800153static int tty_init_io(struct console *console)
Jeremy Kerrd831f962016-01-29 17:18:01 +0800154{
Jeremy Kerr1a0e03b2016-03-08 17:57:11 +0800155 if (console->tty_sirq)
156 tty_set_sysfs_attr(console, "sirq", console->tty_sirq);
157 if (console->tty_lpc_addr)
158 tty_set_sysfs_attr(console, "lpc_address",
159 console->tty_lpc_addr);
160 tty_set_sysfs_attr(console, "enabled", 1);
Jeremy Kerr957818b2016-03-08 14:35:15 +0800161
Jeremy Kerr1a0e03b2016-03-08 17:57:11 +0800162 console->tty_fd = open(console->tty_dev, O_RDWR);
163 if (console->tty_fd <= 0) {
164 warn("Can't open tty %s", console->tty_dev);
Jeremy Kerrd831f962016-01-29 17:18:01 +0800165 return -1;
166 }
167
168 /* Disable character delay. We may want to later enable this when
169 * we detect larger amounts of data
170 */
Jeremy Kerr1a0e03b2016-03-08 17:57:11 +0800171 fcntl(console->tty_fd, F_SETFL, FNDELAY);
Jeremy Kerrd831f962016-01-29 17:18:01 +0800172
Jeremy Kerr329a35f2016-03-10 15:36:01 +0800173 console->pollfds[console->n_pollers].fd = console->tty_fd;
174 console->pollfds[console->n_pollers].events = POLLIN;
175
Jeremy Kerrd831f962016-01-29 17:18:01 +0800176 return 0;
177}
178
Jeremy Kerrd66195c2016-03-16 17:24:51 +0800179static int tty_init(struct console *console, struct config *config)
180{
181 const char *val;
182 char *endp;
183 int rc;
184
185 console->tty_kname = config_get_value(config, "device");
186
187 val = config_get_value(config, "lpc-address");
188 if (val) {
189 console->tty_lpc_addr = strtoul(val, &endp, 0);
190 if (endp == optarg) {
191 warn("Invalid LPC address: '%s'", val);
192 return -1;
193 }
194 }
195
196 val = config_get_value(config, "sirq");
197 if (val) {
198 console->tty_sirq = strtoul(val, &endp, 0);
199 if (endp == optarg)
200 warn("Invalid sirq: '%s'", val);
201 }
202
203 if (!console->tty_kname) {
204 warnx("Error: No TTY device specified");
205 return -1;
206 }
207
208 rc = tty_find_device(console);
209 if (rc)
210 return rc;
211
212 rc = tty_init_io(console);
213 return rc;
214}
215
Jeremy Kerr1a0e03b2016-03-08 17:57:11 +0800216
217int console_data_out(struct console *console, const uint8_t *data, size_t len)
Jeremy Kerrd831f962016-01-29 17:18:01 +0800218{
Jeremy Kerr1a0e03b2016-03-08 17:57:11 +0800219 return write_buf_to_fd(console->tty_fd, data, len);
Jeremy Kerrd831f962016-01-29 17:18:01 +0800220}
221
Jeremy Kerrd47963e2016-03-16 17:29:55 +0800222static void handlers_init(struct console *console, struct config *config)
Jeremy Kerrd831f962016-01-29 17:18:01 +0800223{
Jeremy Kerr1a0e03b2016-03-08 17:57:11 +0800224 extern struct handler *__start_handlers, *__stop_handlers;
225 struct handler *handler;
226 int i;
Jeremy Kerrd831f962016-01-29 17:18:01 +0800227
Jeremy Kerr1a0e03b2016-03-08 17:57:11 +0800228 console->n_handlers = &__stop_handlers - &__start_handlers;
229 console->handlers = &__start_handlers;
Jeremy Kerrd831f962016-01-29 17:18:01 +0800230
Jeremy Kerr1a0e03b2016-03-08 17:57:11 +0800231 printf("%d handler%s\n", console->n_handlers,
232 console->n_handlers == 1 ? "" : "s");
Jeremy Kerrd831f962016-01-29 17:18:01 +0800233
Jeremy Kerr1a0e03b2016-03-08 17:57:11 +0800234 for (i = 0; i < console->n_handlers; i++) {
235 handler = console->handlers[i];
236
237 printf(" %s\n", handler->name);
238
239 if (handler->init)
Jeremy Kerrd47963e2016-03-16 17:29:55 +0800240 handler->init(handler, console, config);
Jeremy Kerrd831f962016-01-29 17:18:01 +0800241 }
Jeremy Kerrd831f962016-01-29 17:18:01 +0800242}
243
Jeremy Kerr1a0e03b2016-03-08 17:57:11 +0800244static void handlers_fini(struct console *console)
Jeremy Kerrd831f962016-01-29 17:18:01 +0800245{
Jeremy Kerr1a0e03b2016-03-08 17:57:11 +0800246 struct handler *handler;
247 int i;
248
249 for (i = 0; i < console->n_handlers; i++) {
250 handler = console->handlers[i];
251 if (handler->fini)
252 handler->fini(handler);
253 }
Jeremy Kerrd831f962016-01-29 17:18:01 +0800254}
255
Jeremy Kerr1a0e03b2016-03-08 17:57:11 +0800256static int handlers_data_in(struct console *console, uint8_t *buf, size_t len)
Jeremy Kerrd831f962016-01-29 17:18:01 +0800257{
Jeremy Kerr1a0e03b2016-03-08 17:57:11 +0800258 struct handler *handler;
259 int i, rc, tmp;
Jeremy Kerrd831f962016-01-29 17:18:01 +0800260
Jeremy Kerr1a0e03b2016-03-08 17:57:11 +0800261 rc = 0;
262
263 for (i = 0; i < console->n_handlers; i++) {
264 handler = console->handlers[i];
265
266 if (!handler->data_in)
267 continue;
268
269 tmp = handler->data_in(handler, buf, len);
270 if (tmp == HANDLER_EXIT)
271 rc = 1;
Jeremy Kerrd831f962016-01-29 17:18:01 +0800272 }
273
Jeremy Kerr1a0e03b2016-03-08 17:57:11 +0800274 return rc;
Jeremy Kerr329a35f2016-03-10 15:36:01 +0800275
Jeremy Kerrd831f962016-01-29 17:18:01 +0800276}
277
Jeremy Kerr329a35f2016-03-10 15:36:01 +0800278struct poller *console_register_poller(struct console *console,
279 struct handler *handler, poller_fn_t poller_fn,
280 int fd, int events, void *data)
Jeremy Kerrd831f962016-01-29 17:18:01 +0800281{
Jeremy Kerr329a35f2016-03-10 15:36:01 +0800282 struct poller *poller;
283 int n;
284
285 poller = malloc(sizeof(*poller));
286 poller->remove = false;
287 poller->handler = handler;
288 poller->fn = poller_fn;
289 poller->data = data;
290
291 /* add one to our pollers array */
292 n = console->n_pollers++;
293 console->pollers = realloc(console->pollers,
294 sizeof(*console->pollers) * console->n_pollers);
295
296 console->pollers[n] = poller;
297
298 /* increase pollfds array too */
299 console->pollfds = realloc(console->pollfds,
300 sizeof(*console->pollfds) *
301 (n_internal_pollfds + console->n_pollers));
302
303 /* shift the end pollfds up by one */
304 memcpy(&console->pollfds[n+n_internal_pollfds],
305 &console->pollfds[n],
306 sizeof(*console->pollfds) * n_internal_pollfds);
307
308 console->pollfds[n].fd = fd;
309 console->pollfds[n].events = events;
310
311 return poller;
312}
313
314void console_unregister_poller(struct console *console,
315 struct poller *poller)
316{
317 int i;
318
319 /* find the entry in our pollers array */
320 for (i = 0; i < console->n_pollers; i++)
321 if (console->pollers[i] == poller)
322 break;
323
324 assert(i < console->n_pollers);
325
326 console->n_pollers--;
327
328 /* remove the item from the pollers array... */
329 memmove(&console->pollers[i], &console->pollers[i+1],
330 sizeof(*console->pollers)
331 * (console->n_pollers - i));
332
333 console->pollers = realloc(console->pollers,
334 sizeof(*console->pollers) * console->n_pollers);
335
336 /* ... and the pollfds array */
337 memmove(&console->pollfds[i], &console->pollfds[i+1],
338 sizeof(*console->pollfds) *
339 (n_internal_pollfds + console->n_pollers - i));
340
341 console->pollfds = realloc(console->pollfds,
342 sizeof(*console->pollfds) *
343 (n_internal_pollfds + console->n_pollers));
344
345
346 free(poller);
347}
348
349static int call_pollers(struct console *console)
350{
351 struct poller *poller;
352 struct pollfd *pollfd;
353 enum poller_ret prc;
354 int i, rc;
Jeremy Kerrd831f962016-01-29 17:18:01 +0800355
Jeremy Kerr1a0e03b2016-03-08 17:57:11 +0800356 rc = 0;
357
Jeremy Kerr329a35f2016-03-10 15:36:01 +0800358 /*
359 * Process poll events by iterating through the pollers and pollfds
360 * in-step, calling any pollers that we've found revents for.
361 */
362 for (i = 0; i < console->n_pollers; i++) {
363 poller = console->pollers[i];
364 pollfd = &console->pollfds[i];
Jeremy Kerr1a0e03b2016-03-08 17:57:11 +0800365
Jeremy Kerr329a35f2016-03-10 15:36:01 +0800366 if (!pollfd->revents)
Jeremy Kerr1a0e03b2016-03-08 17:57:11 +0800367 continue;
368
Jeremy Kerr329a35f2016-03-10 15:36:01 +0800369 prc = poller->fn(poller->handler, pollfd->revents,
370 poller->data);
371 if (prc == POLLER_EXIT)
372 rc = -1;
373 else if (prc == POLLER_REMOVE)
374 poller->remove = true;
375 }
376
377 /**
378 * Process deferred removals; restarting each time we unregister, as
379 * the array will have changed
380 */
381 for (;;) {
382 bool removed = false;
383
384 for (i = 0; i < console->n_pollers; i++) {
385 poller = console->pollers[i];
386 if (poller->remove) {
387 console_unregister_poller(console, poller);
388 removed = true;
389 break;
390 }
391 }
392 if (!removed)
393 break;
Jeremy Kerr1a0e03b2016-03-08 17:57:11 +0800394 }
395
396 return rc;
397}
398
Jeremy Kerr769cee12016-03-15 17:53:56 +0800399static void sighandler(int signal)
400{
401 if (signal == SIGINT)
402 sigint = true;
403}
404
Jeremy Kerr1a0e03b2016-03-08 17:57:11 +0800405int run_console(struct console *console)
406{
Jeremy Kerr769cee12016-03-15 17:53:56 +0800407 sighandler_t sighandler_save;
Jeremy Kerr329a35f2016-03-10 15:36:01 +0800408 int rc;
Jeremy Kerrd831f962016-01-29 17:18:01 +0800409
Jeremy Kerr769cee12016-03-15 17:53:56 +0800410 sighandler_save = signal(SIGINT, sighandler);
411
412 rc = 0;
413
Jeremy Kerrd831f962016-01-29 17:18:01 +0800414 for (;;) {
415 uint8_t buf[4096];
416
Jeremy Kerr769cee12016-03-15 17:53:56 +0800417 if (sigint) {
418 fprintf(stderr, "Received interrupt, exiting\n");
419 break;
420 }
421
Jeremy Kerr329a35f2016-03-10 15:36:01 +0800422 rc = poll(console->pollfds,
423 console->n_pollers + n_internal_pollfds, -1);
Jeremy Kerrd831f962016-01-29 17:18:01 +0800424 if (rc < 0) {
Jeremy Kerr769cee12016-03-15 17:53:56 +0800425 if (errno == EINTR) {
426 continue;
427 } else {
428 warn("poll error");
429 break;
430 }
Jeremy Kerrd831f962016-01-29 17:18:01 +0800431 }
432
Jeremy Kerr329a35f2016-03-10 15:36:01 +0800433 /* process internal fd first */
434 BUILD_ASSERT(n_internal_pollfds == 1);
435
436 if (console->pollfds[console->n_pollers].revents) {
Jeremy Kerr1a0e03b2016-03-08 17:57:11 +0800437 rc = read(console->tty_fd, buf, sizeof(buf));
Jeremy Kerrd831f962016-01-29 17:18:01 +0800438 if (rc <= 0) {
439 warn("Error reading from tty device");
Jeremy Kerr769cee12016-03-15 17:53:56 +0800440 rc = -1;
441 break;
Jeremy Kerrd831f962016-01-29 17:18:01 +0800442 }
Jeremy Kerr1a0e03b2016-03-08 17:57:11 +0800443 rc = handlers_data_in(console, buf, rc);
444 if (rc)
Jeremy Kerr769cee12016-03-15 17:53:56 +0800445 break;
Jeremy Kerr1a0e03b2016-03-08 17:57:11 +0800446 }
Jeremy Kerrd831f962016-01-29 17:18:01 +0800447
Jeremy Kerr329a35f2016-03-10 15:36:01 +0800448 /* ... and then the pollers */
449 rc = call_pollers(console);
Jeremy Kerr1a0e03b2016-03-08 17:57:11 +0800450 if (rc)
Jeremy Kerr769cee12016-03-15 17:53:56 +0800451 break;
Jeremy Kerrd831f962016-01-29 17:18:01 +0800452 }
Jeremy Kerr769cee12016-03-15 17:53:56 +0800453
454 signal(SIGINT, sighandler_save);
455
456 return rc ? -1 : 0;
Jeremy Kerrd831f962016-01-29 17:18:01 +0800457}
Jeremy Kerrd831f962016-01-29 17:18:01 +0800458static const struct option options[] = {
Jeremy Kerrd66195c2016-03-16 17:24:51 +0800459 { "config", required_argument, 0, 'c'},
Jeremy Kerre3f94542016-03-17 16:38:37 +0800460 { 0 },
Jeremy Kerrd831f962016-01-29 17:18:01 +0800461};
462
463int main(int argc, char **argv)
464{
Jeremy Kerrd66195c2016-03-16 17:24:51 +0800465 const char *config_filename = NULL;
Jeremy Kerr1a0e03b2016-03-08 17:57:11 +0800466 struct console *console;
Jeremy Kerrd66195c2016-03-16 17:24:51 +0800467 struct config *config;
468 int rc;
Jeremy Kerrd831f962016-01-29 17:18:01 +0800469
Jeremy Kerr957818b2016-03-08 14:35:15 +0800470 rc = -1;
Jeremy Kerrd831f962016-01-29 17:18:01 +0800471
472 for (;;) {
473 int c, idx;
474
Jeremy Kerrd66195c2016-03-16 17:24:51 +0800475 c = getopt_long(argc, argv, "c:", options, &idx);
Jeremy Kerrd831f962016-01-29 17:18:01 +0800476 if (c == -1)
477 break;
478
479 switch (c) {
Jeremy Kerrd66195c2016-03-16 17:24:51 +0800480 case 'c':
481 config_filename = optarg;
Jeremy Kerrd831f962016-01-29 17:18:01 +0800482 break;
Jeremy Kerrd831f962016-01-29 17:18:01 +0800483 case 'h':
484 case '?':
485 usage(argv[0]);
Jeremy Kerrd66195c2016-03-16 17:24:51 +0800486 return EXIT_SUCCESS;
Jeremy Kerrd831f962016-01-29 17:18:01 +0800487 }
488 }
489
Jeremy Kerrd66195c2016-03-16 17:24:51 +0800490 console = malloc(sizeof(struct console));
491 memset(console, 0, sizeof(*console));
Jeremy Kerr329a35f2016-03-10 15:36:01 +0800492 console->pollfds = calloc(n_internal_pollfds,
493 sizeof(*console->pollfds));
494
Jeremy Kerrd66195c2016-03-16 17:24:51 +0800495 config = config_init(config_filename);
496 if (!config) {
497 warnx("Can't read configuration, exiting.");
498 goto out_free;
Jeremy Kerrd831f962016-01-29 17:18:01 +0800499 }
500
Jeremy Kerrd66195c2016-03-16 17:24:51 +0800501 rc = tty_init(console, config);
Jeremy Kerr17217842016-01-29 18:44:21 +0800502 if (rc)
Jeremy Kerrd66195c2016-03-16 17:24:51 +0800503 goto out_config_fini;
Jeremy Kerrd831f962016-01-29 17:18:01 +0800504
Jeremy Kerrd47963e2016-03-16 17:29:55 +0800505 handlers_init(console, config);
Jeremy Kerrd831f962016-01-29 17:18:01 +0800506
Jeremy Kerr1a0e03b2016-03-08 17:57:11 +0800507 rc = run_console(console);
Jeremy Kerrd831f962016-01-29 17:18:01 +0800508
Jeremy Kerr1a0e03b2016-03-08 17:57:11 +0800509 handlers_fini(console);
Jeremy Kerrd831f962016-01-29 17:18:01 +0800510
Jeremy Kerrd66195c2016-03-16 17:24:51 +0800511out_config_fini:
512 config_fini(config);
513
Jeremy Kerr957818b2016-03-08 14:35:15 +0800514out_free:
Jeremy Kerr89ea8192016-03-15 17:57:43 +0800515 free(console->pollers);
516 free(console->pollfds);
Jeremy Kerr1a0e03b2016-03-08 17:57:11 +0800517 free(console->tty_sysfs_devnode);
518 free(console->tty_dev);
519 free(console);
Jeremy Kerrd831f962016-01-29 17:18:01 +0800520
521 return rc == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
522}