blob: 11d017b82682679785344e3e23e23fbafaee5c66 [file] [log] [blame]
Jeremy Kerrd831f962016-01-29 17:18:01 +08001/**
2 * Console server process for OpenBMC
3 *
Jeremy Kerr9326d772016-03-17 17:15:02 +08004 * Copyright © 2016 IBM Corporation
5 *
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
Jeremy Kerrd831f962016-01-29 17:18:01 +080017 */
18
Jeremy Kerr17217842016-01-29 18:44:21 +080019#define _GNU_SOURCE
20
Jeremy Kerr329a35f2016-03-10 15:36:01 +080021#include <assert.h>
Jeremy Kerr769cee12016-03-15 17:53:56 +080022#include <errno.h>
23#include <signal.h>
Jeremy Kerrd831f962016-01-29 17:18:01 +080024#include <stdint.h>
25#include <stdbool.h>
26#include <stdlib.h>
27#include <stdio.h>
28#include <fcntl.h>
29#include <unistd.h>
30#include <err.h>
Jeremy Kerrd831f962016-01-29 17:18:01 +080031#include <string.h>
32#include <getopt.h>
Jeremy Kerr17217842016-01-29 18:44:21 +080033#include <limits.h>
Johnathan Mantey1cecc5d2019-02-28 15:01:46 -080034#include <time.h>
Jeremy Kerr54e95692016-03-24 17:07:56 +080035#include <termios.h>
Jeremy Kerrd831f962016-01-29 17:18:01 +080036
37#include <sys/types.h>
Johnathan Mantey1cecc5d2019-02-28 15:01:46 -080038#include <sys/time.h>
Joel Stanley87e344c2016-09-01 00:00:51 +093039#include <poll.h>
Jeremy Kerrd831f962016-01-29 17:18:01 +080040
Jeremy Kerr1a0e03b2016-03-08 17:57:11 +080041#include "console-server.h"
Jeremy Kerrd831f962016-01-29 17:18:01 +080042
Cheng C Yangf9c8f6c2019-03-04 18:39:52 +080043#define DBUS_ERR "org.openbmc.error"
44#define DBUS_NAME "xyz.openbmc_project.console"
45#define OBJ_NAME "/xyz/openbmc_project/console"
46
Jeremy Kerr1a0e03b2016-03-08 17:57:11 +080047struct console {
Jeremy Kerr17217842016-01-29 18:44:21 +080048 const char *tty_kname;
49 char *tty_sysfs_devnode;
50 char *tty_dev;
Jeremy Kerr957818b2016-03-08 14:35:15 +080051 int tty_sirq;
52 int tty_lpc_addr;
Benjamin Fairc7fbcd42018-06-04 14:39:01 -070053 speed_t tty_baud;
Jeremy Kerrd831f962016-01-29 17:18:01 +080054 int tty_fd;
Jeremy Kerr329a35f2016-03-10 15:36:01 +080055
Jeremy Kerrf733c852017-02-07 18:40:10 +080056 struct ringbuffer *rb;
57
Jeremy Kerr1a0e03b2016-03-08 17:57:11 +080058 struct handler **handlers;
59 int n_handlers;
Jeremy Kerr329a35f2016-03-10 15:36:01 +080060
61 struct poller **pollers;
62 int n_pollers;
63
64 struct pollfd *pollfds;
Cheng C Yangf9c8f6c2019-03-04 18:39:52 +080065 struct sd_bus *bus;
Jeremy Kerrd831f962016-01-29 17:18:01 +080066};
67
Jeremy Kerr329a35f2016-03-10 15:36:01 +080068struct poller {
69 struct handler *handler;
70 void *data;
Johnathan Mantey1cecc5d2019-02-28 15:01:46 -080071 poller_event_fn_t event_fn;
72 poller_timeout_fn_t timeout_fn;
73 struct timeval timeout;
Jeremy Kerr329a35f2016-03-10 15:36:01 +080074 bool remove;
75};
76
Cheng C Yangf9c8f6c2019-03-04 18:39:52 +080077/* we have two extra entry in the pollfds array for the VUART tty */
78enum internal_pollfds {
79 POLLFD_HOSTTTY = 0,
80 POLLFD_DBUS = 1,
81 MAX_INTERNAL_POLLFD = 2,
82};
Jeremy Kerr329a35f2016-03-10 15:36:01 +080083
Jeremy Kerrf733c852017-02-07 18:40:10 +080084/* size of the shared backlog ringbuffer */
85const size_t buffer_size = 128 * 1024;
86
Jeremy Kerr769cee12016-03-15 17:53:56 +080087/* state shared with the signal handler */
88static bool sigint;
Jeremy Kerr329a35f2016-03-10 15:36:01 +080089
Jeremy Kerrd831f962016-01-29 17:18:01 +080090static void usage(const char *progname)
91{
92 fprintf(stderr,
Vishwanatha Subbanna6221ce92016-07-20 05:35:45 -050093"usage: %s [options] <DEVICE>\n"
Jeremy Kerrd831f962016-01-29 17:18:01 +080094"\n"
95"Options:\n"
Jeremy Kerrd66195c2016-03-16 17:24:51 +080096" --config <FILE> Use FILE for configuration\n"
Jeremy Kerrd831f962016-01-29 17:18:01 +080097"",
98 progname);
99}
100
Jeremy Kerr17217842016-01-29 18:44:21 +0800101/* populates tty_dev and tty_sysfs_devnode, using the tty kernel name */
Jeremy Kerr1a0e03b2016-03-08 17:57:11 +0800102static int tty_find_device(struct console *console)
Jeremy Kerr17217842016-01-29 18:44:21 +0800103{
104 char *tty_class_device_link;
105 char *tty_device_tty_dir;
106 char *tty_device_reldir;
Yi Li45ad7672016-10-18 23:21:19 +0800107 char *tty_path_input;
108 char *tty_path_input_real;
109 char *tty_kname_real;
Jeremy Kerr17217842016-01-29 18:44:21 +0800110 int rc;
111
Jeremy Kerr17217842016-01-29 18:44:21 +0800112 tty_class_device_link = NULL;
113 tty_device_tty_dir = NULL;
114 tty_device_reldir = NULL;
Yi Li45ad7672016-10-18 23:21:19 +0800115 tty_path_input = NULL;
116 tty_path_input_real = NULL;
117 tty_kname_real = NULL;
Jeremy Kerr17217842016-01-29 18:44:21 +0800118
Yi Li45ad7672016-10-18 23:21:19 +0800119 /* udev may rename the tty name with a symbol link, try to resolve */
120 rc = asprintf(&tty_path_input, "/dev/%s", console->tty_kname);
Jeremy Kerr17217842016-01-29 18:44:21 +0800121 if (rc < 0)
122 return -1;
123
Yi Li45ad7672016-10-18 23:21:19 +0800124 tty_path_input_real = realpath(tty_path_input, NULL);
125 if (!tty_path_input_real) {
126 warn("Can't find realpath for /dev/%s", console->tty_kname);
127 goto out_free;
128 }
129
130 tty_kname_real = basename(tty_path_input_real);
131 if (!tty_kname_real) {
132 warn("Can't find real name for /dev/%s", console->tty_kname);
133 goto out_free;
134 }
135
136 rc = asprintf(&tty_class_device_link,
137 "/sys/class/tty/%s", tty_kname_real);
138 if (rc < 0)
139 goto out_free;
140
Jeremy Kerr17217842016-01-29 18:44:21 +0800141 tty_device_tty_dir = realpath(tty_class_device_link, NULL);
Yi Li45ad7672016-10-18 23:21:19 +0800142 if (!tty_device_tty_dir) {
143 warn("Can't query sysfs for device %s", tty_kname_real);
Jeremy Kerr17217842016-01-29 18:44:21 +0800144 goto out_free;
145 }
146
147 rc = asprintf(&tty_device_reldir, "%s/../../", tty_device_tty_dir);
148 if (rc < 0)
149 goto out_free;
150
Jeremy Kerr1a0e03b2016-03-08 17:57:11 +0800151 console->tty_sysfs_devnode = realpath(tty_device_reldir, NULL);
152 if (!console->tty_sysfs_devnode)
Yi Li45ad7672016-10-18 23:21:19 +0800153 warn("Can't find parent device for %s", tty_kname_real);
Jeremy Kerr17217842016-01-29 18:44:21 +0800154
Yi Li45ad7672016-10-18 23:21:19 +0800155 rc = asprintf(&console->tty_dev, "/dev/%s", tty_kname_real);
Jeremy Kerr17217842016-01-29 18:44:21 +0800156 if (rc < 0)
157 goto out_free;
158
159 rc = 0;
160
161out_free:
162 free(tty_class_device_link);
163 free(tty_device_tty_dir);
164 free(tty_device_reldir);
Yi Li45ad7672016-10-18 23:21:19 +0800165 free(tty_path_input);
166 free(tty_path_input_real);
Jeremy Kerr17217842016-01-29 18:44:21 +0800167 return rc;
168}
169
Jeremy Kerr1a0e03b2016-03-08 17:57:11 +0800170static int tty_set_sysfs_attr(struct console *console, const char *name,
Jeremy Kerr957818b2016-03-08 14:35:15 +0800171 int value)
172{
173 char *path;
174 FILE *fp;
175 int rc;
176
Jeremy Kerr1a0e03b2016-03-08 17:57:11 +0800177 rc = asprintf(&path, "%s/%s", console->tty_sysfs_devnode, name);
Jeremy Kerr957818b2016-03-08 14:35:15 +0800178 if (rc < 0)
179 return -1;
180
181 fp = fopen(path, "w");
182 if (!fp) {
183 warn("Can't access attribute %s on device %s",
Jeremy Kerr1a0e03b2016-03-08 17:57:11 +0800184 name, console->tty_kname);
Jeremy Kerr957818b2016-03-08 14:35:15 +0800185 rc = -1;
186 goto out_free;
187 }
188 setvbuf(fp, NULL, _IONBF, 0);
189
190 rc = fprintf(fp, "0x%x", value);
191 if (rc < 0)
192 warn("Error writing to %s attribute of device %s",
Jeremy Kerr1a0e03b2016-03-08 17:57:11 +0800193 name, console->tty_kname);
Jeremy Kerr957818b2016-03-08 14:35:15 +0800194 fclose(fp);
195
196
197
198out_free:
199 free(path);
200 return rc;
201}
202
Jeremy Kerrd831f962016-01-29 17:18:01 +0800203/**
Benjamin Fairc7fbcd42018-06-04 14:39:01 -0700204 * Set termios attributes on the console tty.
Jeremy Kerr54e95692016-03-24 17:07:56 +0800205 */
206static void tty_init_termios(struct console *console)
207{
208 struct termios termios;
209 int rc;
210
211 rc = tcgetattr(console->tty_fd, &termios);
212 if (rc) {
213 warn("Can't read tty termios");
214 return;
215 }
216
Benjamin Fairc7fbcd42018-06-04 14:39:01 -0700217 if (console->tty_baud) {
218 if (cfsetspeed(&termios, console->tty_baud) < 0)
219 warn("Couldn't set speeds for %s", console->tty_kname);
220 }
221
222 /* Set console to raw mode: we don't want any processing to occur on
223 * the underlying terminal input/output.
224 */
Jeremy Kerr54e95692016-03-24 17:07:56 +0800225 cfmakeraw(&termios);
Benjamin Fairc7fbcd42018-06-04 14:39:01 -0700226
Jeremy Kerr54e95692016-03-24 17:07:56 +0800227 rc = tcsetattr(console->tty_fd, TCSANOW, &termios);
228 if (rc)
Benjamin Fairc7fbcd42018-06-04 14:39:01 -0700229 warn("Can't set terminal options for %s", console->tty_kname);
Jeremy Kerr54e95692016-03-24 17:07:56 +0800230}
231
Cheng C Yangf9c8f6c2019-03-04 18:39:52 +0800232
233static void tty_change_baudrate(struct console *console)
234{
235 struct handler *handler;
236 int i, rc;
237
238 tty_init_termios(console);
239
240 for (i = 0; i < console->n_handlers; i++) {
241 handler = console->handlers[i];
242 if (!handler->baudrate)
243 continue;
244
245 rc = handler->baudrate(handler, console->tty_baud);
246 if (rc)
247 warnx("Can't set terminal baudrate for handler %s",
248 handler->name);
249 }
250}
251
Jeremy Kerr54e95692016-03-24 17:07:56 +0800252/**
Jeremy Kerrd831f962016-01-29 17:18:01 +0800253 * Open and initialise the serial device
254 */
Jeremy Kerr1a0e03b2016-03-08 17:57:11 +0800255static int tty_init_io(struct console *console)
Jeremy Kerrd831f962016-01-29 17:18:01 +0800256{
Jeremy Kerr1a0e03b2016-03-08 17:57:11 +0800257 if (console->tty_sirq)
258 tty_set_sysfs_attr(console, "sirq", console->tty_sirq);
259 if (console->tty_lpc_addr)
260 tty_set_sysfs_attr(console, "lpc_address",
261 console->tty_lpc_addr);
Jeremy Kerr957818b2016-03-08 14:35:15 +0800262
Jeremy Kerr1a0e03b2016-03-08 17:57:11 +0800263 console->tty_fd = open(console->tty_dev, O_RDWR);
264 if (console->tty_fd <= 0) {
265 warn("Can't open tty %s", console->tty_dev);
Jeremy Kerrd831f962016-01-29 17:18:01 +0800266 return -1;
267 }
268
269 /* Disable character delay. We may want to later enable this when
270 * we detect larger amounts of data
271 */
Jeremy Kerr1a0e03b2016-03-08 17:57:11 +0800272 fcntl(console->tty_fd, F_SETFL, FNDELAY);
Jeremy Kerrd831f962016-01-29 17:18:01 +0800273
Jeremy Kerr54e95692016-03-24 17:07:56 +0800274 tty_init_termios(console);
275
Jeremy Kerr329a35f2016-03-10 15:36:01 +0800276 console->pollfds[console->n_pollers].fd = console->tty_fd;
277 console->pollfds[console->n_pollers].events = POLLIN;
278
Jeremy Kerrd831f962016-01-29 17:18:01 +0800279 return 0;
280}
281
Jeremy Kerrd66195c2016-03-16 17:24:51 +0800282static int tty_init(struct console *console, struct config *config)
283{
284 const char *val;
285 char *endp;
286 int rc;
287
Jeremy Kerrd66195c2016-03-16 17:24:51 +0800288 val = config_get_value(config, "lpc-address");
289 if (val) {
290 console->tty_lpc_addr = strtoul(val, &endp, 0);
291 if (endp == optarg) {
292 warn("Invalid LPC address: '%s'", val);
293 return -1;
294 }
295 }
296
297 val = config_get_value(config, "sirq");
298 if (val) {
299 console->tty_sirq = strtoul(val, &endp, 0);
300 if (endp == optarg)
301 warn("Invalid sirq: '%s'", val);
302 }
303
Benjamin Fairc7fbcd42018-06-04 14:39:01 -0700304 val = config_get_value(config, "baud");
305 if (val) {
306 if (config_parse_baud(&console->tty_baud, val))
307 warnx("Invalid baud rate: '%s'", val);
308 }
309
Jeremy Kerrd66195c2016-03-16 17:24:51 +0800310 if (!console->tty_kname) {
311 warnx("Error: No TTY device specified");
312 return -1;
313 }
314
315 rc = tty_find_device(console);
316 if (rc)
317 return rc;
318
319 rc = tty_init_io(console);
320 return rc;
321}
322
Jeremy Kerr1a0e03b2016-03-08 17:57:11 +0800323
324int console_data_out(struct console *console, const uint8_t *data, size_t len)
Jeremy Kerrd831f962016-01-29 17:18:01 +0800325{
Jeremy Kerr1a0e03b2016-03-08 17:57:11 +0800326 return write_buf_to_fd(console->tty_fd, data, len);
Jeremy Kerrd831f962016-01-29 17:18:01 +0800327}
328
Cheng C Yangf9c8f6c2019-03-04 18:39:52 +0800329static int method_set_baud_rate(sd_bus_message *msg, void *userdata,
330 sd_bus_error *err)
331{
332 struct console *console = userdata;
333 uint32_t baudrate;
334 speed_t speed;
335 int r;
336
337 if (!console) {
338 sd_bus_error_set_const(err, DBUS_ERR, "Internal error");
339 return sd_bus_reply_method_return(msg, "x", 0);
340 }
341
342 r = sd_bus_message_read(msg, "u", &baudrate);
343 if (r < 0) {
344 sd_bus_error_set_const(err, DBUS_ERR, "Bad message");
345 return sd_bus_reply_method_return(msg, "x", -EINVAL);
346 }
347
348 speed = parse_int_to_baud(baudrate);
349 if (!speed) {
350 warnx("Invalid baud rate: '%u'", baudrate);
351 return sd_bus_reply_method_return(msg, "x", -EINVAL);
352 }
353
354 console->tty_baud = speed;
355 tty_change_baudrate(console);
356
357 return sd_bus_reply_method_return(msg, "x", r);
358}
359
360static int get_handler(sd_bus *bus, const char *path, const char *interface,
361 const char *property, sd_bus_message *reply, void *userdata,
362 sd_bus_error *error) {
363 struct console *console = userdata;
364 uint32_t baudrate;
365 int r;
366
367 baudrate = parse_baud_to_int(console->tty_baud);
368 if (!baudrate)
369 warnx("Invalid baud rate: '%d'", console->tty_baud);
370
371 r = sd_bus_message_append(reply, "u", baudrate);
372
373 return r;
374}
375
376static const sd_bus_vtable console_vtable[] = {
377 SD_BUS_VTABLE_START(0),
378 SD_BUS_METHOD("setBaudRate", "u", "x", method_set_baud_rate,
379 SD_BUS_VTABLE_UNPRIVILEGED),
380 SD_BUS_PROPERTY("baudrate", "u", get_handler, 0, 0),
381 SD_BUS_VTABLE_END,};
382
383static void dbus_init(struct console *console, struct config *config)
384{
385 int dbus_poller = 0;
386 int fd, r;
387
388 if (!console) {
389 warnx("Couldn't get valid console");
390 return;
391 }
392
393 r = sd_bus_default_system(&console->bus);
394 if (r < 0) {
395 warnx("Failed to connect to system bus: %s", strerror(-r));
396 return;
397 }
398
399 r = sd_bus_add_object_vtable(console->bus, NULL, OBJ_NAME, DBUS_NAME,
400 console_vtable, console);
401 if (r < 0) {
402 warnx("Failed to issue method call: %s", strerror(-r));
403 return;
404 }
405
406 r = sd_bus_request_name(console->bus, DBUS_NAME, SD_BUS_NAME_ALLOW_REPLACEMENT
407 |SD_BUS_NAME_REPLACE_EXISTING);
408 if (r < 0) {
409 warnx("Failed to acquire service name: %s", strerror(-r));
410 return;
411 }
412
413 fd = sd_bus_get_fd(console->bus);
414 if (fd < 0) {
415 warnx("Couldn't get the bus file descriptor");
416 return;
417 }
418
419 dbus_poller = POLLFD_DBUS;
420
421 console->pollfds[dbus_poller].fd = fd;
422 console->pollfds[dbus_poller].events = POLLIN;
423}
424
Jeremy Kerrd47963e2016-03-16 17:29:55 +0800425static void handlers_init(struct console *console, struct config *config)
Jeremy Kerrd831f962016-01-29 17:18:01 +0800426{
Jeremy Kerr1a0e03b2016-03-08 17:57:11 +0800427 extern struct handler *__start_handlers, *__stop_handlers;
428 struct handler *handler;
Jeremy Kerr021b91f2016-04-28 11:51:52 +0800429 int i, rc;
Jeremy Kerrd831f962016-01-29 17:18:01 +0800430
Jeremy Kerr1a0e03b2016-03-08 17:57:11 +0800431 console->n_handlers = &__stop_handlers - &__start_handlers;
432 console->handlers = &__start_handlers;
Jeremy Kerrd831f962016-01-29 17:18:01 +0800433
Jeremy Kerr1a0e03b2016-03-08 17:57:11 +0800434 printf("%d handler%s\n", console->n_handlers,
435 console->n_handlers == 1 ? "" : "s");
Jeremy Kerrd831f962016-01-29 17:18:01 +0800436
Jeremy Kerr1a0e03b2016-03-08 17:57:11 +0800437 for (i = 0; i < console->n_handlers; i++) {
438 handler = console->handlers[i];
439
Jeremy Kerr021b91f2016-04-28 11:51:52 +0800440 rc = 0;
Jeremy Kerr1a0e03b2016-03-08 17:57:11 +0800441 if (handler->init)
Jeremy Kerr021b91f2016-04-28 11:51:52 +0800442 rc = handler->init(handler, console, config);
443
444 handler->active = rc == 0;
445
446 printf(" %s [%sactive]\n", handler->name,
447 handler->active ? "" : "in");
Jeremy Kerrd831f962016-01-29 17:18:01 +0800448 }
Jeremy Kerrd831f962016-01-29 17:18:01 +0800449}
450
Jeremy Kerr1a0e03b2016-03-08 17:57:11 +0800451static void handlers_fini(struct console *console)
Jeremy Kerrd831f962016-01-29 17:18:01 +0800452{
Jeremy Kerr1a0e03b2016-03-08 17:57:11 +0800453 struct handler *handler;
454 int i;
455
456 for (i = 0; i < console->n_handlers; i++) {
457 handler = console->handlers[i];
Jeremy Kerr021b91f2016-04-28 11:51:52 +0800458 if (handler->fini && handler->active)
Jeremy Kerr1a0e03b2016-03-08 17:57:11 +0800459 handler->fini(handler);
460 }
Jeremy Kerrd831f962016-01-29 17:18:01 +0800461}
462
Johnathan Mantey1cecc5d2019-02-28 15:01:46 -0800463static int get_current_time(struct timeval *tv)
464{
465 struct timespec t;
466 int rc;
467
468 /*
469 * We use clock_gettime(CLOCK_MONOTONIC) so we're immune to
470 * local time changes. However, a struct timeval is more
471 * convenient for calculations, so convert to that.
472 */
473 rc = clock_gettime(CLOCK_MONOTONIC, &t);
474 if (rc)
475 return rc;
476
477 tv->tv_sec = t.tv_sec;
478 tv->tv_usec = t.tv_nsec / 1000;
479
480 return 0;
481}
482
Jeremy Kerrf733c852017-02-07 18:40:10 +0800483struct ringbuffer_consumer *console_ringbuffer_consumer_register(
484 struct console *console,
485 ringbuffer_poll_fn_t poll_fn, void *data)
Jeremy Kerrd831f962016-01-29 17:18:01 +0800486{
Jeremy Kerrf733c852017-02-07 18:40:10 +0800487 return ringbuffer_consumer_register(console->rb, poll_fn, data);
Jeremy Kerrd831f962016-01-29 17:18:01 +0800488}
489
Jeremy Kerr55c97122017-02-07 17:06:46 +0800490struct poller *console_poller_register(struct console *console,
Johnathan Mantey1cecc5d2019-02-28 15:01:46 -0800491 struct handler *handler, poller_event_fn_t poller_fn,
492 poller_timeout_fn_t timeout_fn, int fd,
493 int events, void *data)
Jeremy Kerrd831f962016-01-29 17:18:01 +0800494{
Jeremy Kerr329a35f2016-03-10 15:36:01 +0800495 struct poller *poller;
496 int n;
497
498 poller = malloc(sizeof(*poller));
499 poller->remove = false;
500 poller->handler = handler;
Johnathan Mantey1cecc5d2019-02-28 15:01:46 -0800501 poller->event_fn = poller_fn;
502 poller->timeout_fn = timeout_fn;
Jeremy Kerr329a35f2016-03-10 15:36:01 +0800503 poller->data = data;
504
505 /* add one to our pollers array */
506 n = console->n_pollers++;
507 console->pollers = realloc(console->pollers,
508 sizeof(*console->pollers) * console->n_pollers);
509
510 console->pollers[n] = poller;
511
512 /* increase pollfds array too */
513 console->pollfds = realloc(console->pollfds,
514 sizeof(*console->pollfds) *
Cheng C Yangf9c8f6c2019-03-04 18:39:52 +0800515 (MAX_INTERNAL_POLLFD + console->n_pollers));
Jeremy Kerr329a35f2016-03-10 15:36:01 +0800516
517 /* shift the end pollfds up by one */
Cheng C Yangf9c8f6c2019-03-04 18:39:52 +0800518 memcpy(&console->pollfds[n+1],
519 &console->pollfds[n],
520 sizeof(*console->pollfds) * MAX_INTERNAL_POLLFD);
Jeremy Kerr329a35f2016-03-10 15:36:01 +0800521
522 console->pollfds[n].fd = fd;
523 console->pollfds[n].events = events;
524
525 return poller;
526}
527
Jeremy Kerr55c97122017-02-07 17:06:46 +0800528void console_poller_unregister(struct console *console,
Jeremy Kerr329a35f2016-03-10 15:36:01 +0800529 struct poller *poller)
530{
531 int i;
532
533 /* find the entry in our pollers array */
534 for (i = 0; i < console->n_pollers; i++)
535 if (console->pollers[i] == poller)
536 break;
537
538 assert(i < console->n_pollers);
539
540 console->n_pollers--;
541
542 /* remove the item from the pollers array... */
543 memmove(&console->pollers[i], &console->pollers[i+1],
544 sizeof(*console->pollers)
545 * (console->n_pollers - i));
546
547 console->pollers = realloc(console->pollers,
548 sizeof(*console->pollers) * console->n_pollers);
549
550 /* ... and the pollfds array */
551 memmove(&console->pollfds[i], &console->pollfds[i+1],
552 sizeof(*console->pollfds) *
Cheng C Yangf9c8f6c2019-03-04 18:39:52 +0800553 (MAX_INTERNAL_POLLFD + console->n_pollers - i));
Jeremy Kerr329a35f2016-03-10 15:36:01 +0800554
555 console->pollfds = realloc(console->pollfds,
556 sizeof(*console->pollfds) *
Cheng C Yangf9c8f6c2019-03-04 18:39:52 +0800557 (MAX_INTERNAL_POLLFD + console->n_pollers));
Jeremy Kerr329a35f2016-03-10 15:36:01 +0800558
559
560 free(poller);
561}
562
Jeremy Kerr6b1fed22017-02-07 21:40:38 +0800563void console_poller_set_events(struct console *console, struct poller *poller,
564 int events)
565{
566 int i;
567
568 /* find the entry in our pollers array */
569 for (i = 0; i < console->n_pollers; i++)
570 if (console->pollers[i] == poller)
571 break;
572
573 console->pollfds[i].events = events;
574}
575
Johnathan Mantey1cecc5d2019-02-28 15:01:46 -0800576void console_poller_set_timeout(struct console *console, struct poller *poller,
577 const struct timeval *tv)
578{
579 struct timeval now;
580 int rc;
581
582 rc = get_current_time(&now);
583 if (rc)
584 return;
585
586 timeradd(&now, tv, &poller->timeout);
587}
588
589static int get_poll_timeout(struct console *console, struct timeval *cur_time)
590{
591 struct timeval *earliest, interval;
592 struct poller *poller;
593 int i;
594
595 earliest = NULL;
596
597 for (i = 0; i < console->n_pollers; i++) {
598 poller = console->pollers[i];
599
600 if (poller->timeout_fn && timerisset(&poller->timeout) &&
601 (!earliest ||
602 (earliest && timercmp(&poller->timeout, earliest, <)))){
603 // poller is buffering data and needs the poll
604 // function to timeout.
605 earliest = &poller->timeout;
606 }
607 }
608
609 if (earliest) {
610 if (timercmp(earliest, cur_time, >)) {
611 /* recalculate the timeout period, time period has
612 * not elapsed */
613 timersub(earliest, cur_time, &interval);
614 return ((interval.tv_sec * 1000) +
615 (interval.tv_usec / 1000));
616 } else {
617 /* return from poll immediately */
618 return 0;
619 }
620 } else {
621 /* poll indefinitely */
622 return -1;
623 }
624}
625
626static int call_pollers(struct console *console, struct timeval *cur_time)
Jeremy Kerr329a35f2016-03-10 15:36:01 +0800627{
628 struct poller *poller;
629 struct pollfd *pollfd;
630 enum poller_ret prc;
631 int i, rc;
Jeremy Kerrd831f962016-01-29 17:18:01 +0800632
Jeremy Kerr1a0e03b2016-03-08 17:57:11 +0800633 rc = 0;
634
Jeremy Kerr329a35f2016-03-10 15:36:01 +0800635 /*
636 * Process poll events by iterating through the pollers and pollfds
637 * in-step, calling any pollers that we've found revents for.
638 */
639 for (i = 0; i < console->n_pollers; i++) {
640 poller = console->pollers[i];
641 pollfd = &console->pollfds[i];
Johnathan Mantey1cecc5d2019-02-28 15:01:46 -0800642 prc = POLLER_OK;
Jeremy Kerr1a0e03b2016-03-08 17:57:11 +0800643
Johnathan Mantey1cecc5d2019-02-28 15:01:46 -0800644 /* process pending events... */
645 if (pollfd->revents) {
646 prc = poller->event_fn(poller->handler, pollfd->revents,
647 poller->data);
648 if (prc == POLLER_EXIT)
649 rc = -1;
650 else if (prc == POLLER_REMOVE)
651 poller->remove = true;
652 }
Jeremy Kerr1a0e03b2016-03-08 17:57:11 +0800653
Johnathan Mantey1cecc5d2019-02-28 15:01:46 -0800654 if ((prc == POLLER_OK) && poller->timeout_fn &&
655 timerisset(&poller->timeout) &&
656 timercmp(&poller->timeout, cur_time, <=)) {
657 /* One of the ringbuffer consumers is buffering the
658 data stream. The amount of idle time the consumer
659 desired has expired. Process the buffered data for
660 transmission. */
661 timerclear(&poller->timeout);
662 prc = poller->timeout_fn(poller->handler, poller->data);
663 if (prc == POLLER_EXIT) {
664 rc = -1;
665 } else if (prc == POLLER_REMOVE) {
666 poller->remove = true;
667 }
668 }
Jeremy Kerr329a35f2016-03-10 15:36:01 +0800669 }
670
671 /**
672 * Process deferred removals; restarting each time we unregister, as
673 * the array will have changed
674 */
675 for (;;) {
676 bool removed = false;
677
678 for (i = 0; i < console->n_pollers; i++) {
679 poller = console->pollers[i];
680 if (poller->remove) {
Jeremy Kerr55c97122017-02-07 17:06:46 +0800681 console_poller_unregister(console, poller);
Jeremy Kerr329a35f2016-03-10 15:36:01 +0800682 removed = true;
683 break;
684 }
685 }
686 if (!removed)
687 break;
Jeremy Kerr1a0e03b2016-03-08 17:57:11 +0800688 }
689
690 return rc;
691}
692
Jeremy Kerr769cee12016-03-15 17:53:56 +0800693static void sighandler(int signal)
694{
695 if (signal == SIGINT)
696 sigint = true;
697}
698
Jeremy Kerr1a0e03b2016-03-08 17:57:11 +0800699int run_console(struct console *console)
700{
Jeremy Kerr769cee12016-03-15 17:53:56 +0800701 sighandler_t sighandler_save;
Johnathan Mantey1cecc5d2019-02-28 15:01:46 -0800702 struct timeval tv;
703 int rc, timeout;
Jeremy Kerrd831f962016-01-29 17:18:01 +0800704
Jeremy Kerr769cee12016-03-15 17:53:56 +0800705 sighandler_save = signal(SIGINT, sighandler);
706
707 rc = 0;
708
Jeremy Kerrd831f962016-01-29 17:18:01 +0800709 for (;;) {
710 uint8_t buf[4096];
711
Jeremy Kerr17641452017-02-07 22:09:13 +0800712 BUILD_ASSERT(sizeof(buf) <= buffer_size);
713
Jeremy Kerr769cee12016-03-15 17:53:56 +0800714 if (sigint) {
715 fprintf(stderr, "Received interrupt, exiting\n");
716 break;
717 }
718
Johnathan Mantey1cecc5d2019-02-28 15:01:46 -0800719 rc = get_current_time(&tv);
720 if (rc) {
721 warn("Failed to read current time");
722 break;
723 }
724
725 timeout = get_poll_timeout(console, &tv);
726
Jeremy Kerr329a35f2016-03-10 15:36:01 +0800727 rc = poll(console->pollfds,
Johnathan Mantey1cecc5d2019-02-28 15:01:46 -0800728 console->n_pollers + MAX_INTERNAL_POLLFD,
729 timeout);
730
Jeremy Kerrd831f962016-01-29 17:18:01 +0800731 if (rc < 0) {
Jeremy Kerr769cee12016-03-15 17:53:56 +0800732 if (errno == EINTR) {
733 continue;
734 } else {
735 warn("poll error");
736 break;
737 }
Jeremy Kerrd831f962016-01-29 17:18:01 +0800738 }
739
Jeremy Kerr329a35f2016-03-10 15:36:01 +0800740 /* process internal fd first */
Jeremy Kerr329a35f2016-03-10 15:36:01 +0800741 if (console->pollfds[console->n_pollers].revents) {
Jeremy Kerr1a0e03b2016-03-08 17:57:11 +0800742 rc = read(console->tty_fd, buf, sizeof(buf));
Jeremy Kerrd831f962016-01-29 17:18:01 +0800743 if (rc <= 0) {
744 warn("Error reading from tty device");
Jeremy Kerr769cee12016-03-15 17:53:56 +0800745 rc = -1;
746 break;
Jeremy Kerrd831f962016-01-29 17:18:01 +0800747 }
Jeremy Kerrf733c852017-02-07 18:40:10 +0800748 rc = ringbuffer_queue(console->rb, buf, rc);
Jeremy Kerr1a0e03b2016-03-08 17:57:11 +0800749 if (rc)
Jeremy Kerr769cee12016-03-15 17:53:56 +0800750 break;
Jeremy Kerr1a0e03b2016-03-08 17:57:11 +0800751 }
Jeremy Kerrd831f962016-01-29 17:18:01 +0800752
Cheng C Yangf9c8f6c2019-03-04 18:39:52 +0800753 if (console->pollfds[console->n_pollers + 1].revents) {
754 sd_bus_process(console->bus, NULL);
755 }
756
Jeremy Kerr329a35f2016-03-10 15:36:01 +0800757 /* ... and then the pollers */
Johnathan Mantey1cecc5d2019-02-28 15:01:46 -0800758 rc = call_pollers(console, &tv);
Jeremy Kerr1a0e03b2016-03-08 17:57:11 +0800759 if (rc)
Jeremy Kerr769cee12016-03-15 17:53:56 +0800760 break;
Jeremy Kerrd831f962016-01-29 17:18:01 +0800761 }
Jeremy Kerr769cee12016-03-15 17:53:56 +0800762
763 signal(SIGINT, sighandler_save);
Cheng C Yangf9c8f6c2019-03-04 18:39:52 +0800764 sd_bus_unref(console->bus);
Jeremy Kerr769cee12016-03-15 17:53:56 +0800765
766 return rc ? -1 : 0;
Jeremy Kerrd831f962016-01-29 17:18:01 +0800767}
Jeremy Kerrd831f962016-01-29 17:18:01 +0800768static const struct option options[] = {
Jeremy Kerrd66195c2016-03-16 17:24:51 +0800769 { "config", required_argument, 0, 'c'},
Joel Stanleyf5858b52016-03-18 16:33:03 +1030770 { 0, 0, 0, 0},
Jeremy Kerrd831f962016-01-29 17:18:01 +0800771};
772
773int main(int argc, char **argv)
774{
Jeremy Kerrd66195c2016-03-16 17:24:51 +0800775 const char *config_filename = NULL;
Vishwanatha Subbanna6221ce92016-07-20 05:35:45 -0500776 const char *config_tty_kname = NULL;
Jeremy Kerr1a0e03b2016-03-08 17:57:11 +0800777 struct console *console;
Jeremy Kerrd66195c2016-03-16 17:24:51 +0800778 struct config *config;
779 int rc;
Jeremy Kerrd831f962016-01-29 17:18:01 +0800780
Jeremy Kerr957818b2016-03-08 14:35:15 +0800781 rc = -1;
Jeremy Kerrd831f962016-01-29 17:18:01 +0800782
783 for (;;) {
784 int c, idx;
785
Jeremy Kerrd66195c2016-03-16 17:24:51 +0800786 c = getopt_long(argc, argv, "c:", options, &idx);
Jeremy Kerrd831f962016-01-29 17:18:01 +0800787 if (c == -1)
788 break;
789
790 switch (c) {
Jeremy Kerrd66195c2016-03-16 17:24:51 +0800791 case 'c':
792 config_filename = optarg;
Jeremy Kerrd831f962016-01-29 17:18:01 +0800793 break;
Jeremy Kerrd831f962016-01-29 17:18:01 +0800794 case 'h':
795 case '?':
796 usage(argv[0]);
Jeremy Kerrd66195c2016-03-16 17:24:51 +0800797 return EXIT_SUCCESS;
Jeremy Kerrd831f962016-01-29 17:18:01 +0800798 }
799 }
800
Andrew Jeffery91dde142020-02-12 22:46:27 +1030801 if (optind < argc)
802 config_tty_kname = argv[optind];
Vishwanatha Subbanna6221ce92016-07-20 05:35:45 -0500803
Jeremy Kerrd66195c2016-03-16 17:24:51 +0800804 console = malloc(sizeof(struct console));
805 memset(console, 0, sizeof(*console));
Cheng C Yangf9c8f6c2019-03-04 18:39:52 +0800806 console->pollfds = calloc(MAX_INTERNAL_POLLFD,
Jeremy Kerr329a35f2016-03-10 15:36:01 +0800807 sizeof(*console->pollfds));
Jeremy Kerrf733c852017-02-07 18:40:10 +0800808 console->rb = ringbuffer_init(buffer_size);
Jeremy Kerr329a35f2016-03-10 15:36:01 +0800809
Jeremy Kerrd66195c2016-03-16 17:24:51 +0800810 config = config_init(config_filename);
811 if (!config) {
812 warnx("Can't read configuration, exiting.");
813 goto out_free;
Jeremy Kerrd831f962016-01-29 17:18:01 +0800814 }
815
Andrew Jeffery91dde142020-02-12 22:46:27 +1030816 if (!config_tty_kname)
817 config_tty_kname = config_get_value(config, "upstream-tty");
818
819 if (!config_tty_kname) {
820 warnx("No TTY device specified");
821 usage(argv[0]);
822 return EXIT_FAILURE;
823 }
824
Vishwanatha Subbanna6221ce92016-07-20 05:35:45 -0500825 console->tty_kname = config_tty_kname;
826
Jeremy Kerrd66195c2016-03-16 17:24:51 +0800827 rc = tty_init(console, config);
Jeremy Kerr17217842016-01-29 18:44:21 +0800828 if (rc)
Jeremy Kerrd66195c2016-03-16 17:24:51 +0800829 goto out_config_fini;
Jeremy Kerrd831f962016-01-29 17:18:01 +0800830
Cheng C Yangf9c8f6c2019-03-04 18:39:52 +0800831 dbus_init(console, config);
832
Jeremy Kerrd47963e2016-03-16 17:29:55 +0800833 handlers_init(console, config);
Jeremy Kerrd831f962016-01-29 17:18:01 +0800834
Jeremy Kerr1a0e03b2016-03-08 17:57:11 +0800835 rc = run_console(console);
Jeremy Kerrd831f962016-01-29 17:18:01 +0800836
Jeremy Kerr1a0e03b2016-03-08 17:57:11 +0800837 handlers_fini(console);
Jeremy Kerrd831f962016-01-29 17:18:01 +0800838
Jeremy Kerrd66195c2016-03-16 17:24:51 +0800839out_config_fini:
840 config_fini(config);
841
Jeremy Kerr957818b2016-03-08 14:35:15 +0800842out_free:
Jeremy Kerr89ea8192016-03-15 17:57:43 +0800843 free(console->pollers);
844 free(console->pollfds);
Jeremy Kerr1a0e03b2016-03-08 17:57:11 +0800845 free(console->tty_sysfs_devnode);
846 free(console->tty_dev);
847 free(console);
Jeremy Kerrd831f962016-01-29 17:18:01 +0800848
849 return rc == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
850}