blob: bbf7796c5312636e6edb55eb74a820e1e2e005b5 [file] [log] [blame]
Jeremy Kerr3d36ee22019-05-30 11:15:37 +08001/* SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later */
2
Jeremy Kerr8e31cfd2019-05-07 17:13:51 +08003#define _GNU_SOURCE
4
Andrew Jefferyd4103f82021-06-16 14:39:36 +09305#include "config.h"
6
Jeremy Kerr8e31cfd2019-05-07 17:13:51 +08007#include <assert.h>
8#include <err.h>
Andrew Jeffery6896d412020-03-11 09:25:32 +10309#include <errno.h>
Jeremy Kerr8e31cfd2019-05-07 17:13:51 +080010#include <getopt.h>
Andrew Jefferyb93b6112020-06-05 14:13:44 +093011#include <limits.h>
Jeremy Kerr8e31cfd2019-05-07 17:13:51 +080012#include <poll.h>
Andrew Jeffery490e3872021-08-24 21:11:30 +093013#include <signal.h>
Andrew Jeffery04b81fc2020-02-05 13:07:29 +103014#include <stdbool.h>
Jeremy Kerr8e31cfd2019-05-07 17:13:51 +080015#include <stdio.h>
16#include <stdlib.h>
17#include <string.h>
18#include <unistd.h>
19
Andrew Jeffery490e3872021-08-24 21:11:30 +093020#include <sys/signalfd.h>
Jeremy Kerr8e31cfd2019-05-07 17:13:51 +080021#include <sys/socket.h>
22#include <sys/un.h>
23
Andrew Jefferyd4103f82021-06-16 14:39:36 +093024#define SD_LISTEN_FDS_START 3
25
Jeremy Kerr8e31cfd2019-05-07 17:13:51 +080026#include "libmctp.h"
27#include "libmctp-serial.h"
28#include "libmctp-astlpc.h"
29
30#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
Jeremy Kerr34b9b3d2019-11-27 11:27:05 +080031#define __unused __attribute__((unused))
Jeremy Kerr8e31cfd2019-05-07 17:13:51 +080032
Andrew Jefferyd4103f82021-06-16 14:39:36 +093033#if HAVE_SYSTEMD_SD_DAEMON_H
34#include <systemd/sd-daemon.h>
35#else
36static inline int sd_listen_fds(int i __unused)
37{
38 return -1;
39}
40#endif
41
Jeremy Kerr8e31cfd2019-05-07 17:13:51 +080042static const mctp_eid_t local_eid_default = 8;
43static char sockname[] = "\0mctp-mux";
44
45struct binding {
46 const char *name;
47 int (*init)(struct mctp *mctp, struct binding *binding,
48 mctp_eid_t eid, int n_params,
49 char * const * params);
50 int (*get_fd)(struct binding *binding);
51 int (*process)(struct binding *binding);
52 void *data;
53};
54
55struct client {
56 bool active;
57 int sock;
58 uint8_t type;
59};
60
61struct ctx {
62 struct mctp *mctp;
63 struct binding *binding;
64 bool verbose;
65 int local_eid;
Jeremy Kerrf49b2ac2019-08-02 15:10:51 +080066 void *buf;
67 size_t buf_size;
Jeremy Kerr8e31cfd2019-05-07 17:13:51 +080068
69 int sock;
70 struct pollfd *pollfds;
71
72 struct client *clients;
73 int n_clients;
74};
75
76static void tx_message(struct ctx *ctx, mctp_eid_t eid, void *msg, size_t len)
77{
Andrew Jeffery06735052021-01-27 23:50:55 +103078 int rc;
79
80 rc = mctp_message_tx(ctx->mctp, eid, msg, len);
81 if (rc)
82 warnx("Failed to send message: %d", rc);
Jeremy Kerr8e31cfd2019-05-07 17:13:51 +080083}
84
85static void client_remove_inactive(struct ctx *ctx)
86{
87 int i;
88
89 for (i = 0; i < ctx->n_clients; i++) {
90 struct client *client = &ctx->clients[i];
91 if (client->active)
92 continue;
93 close(client->sock);
94
95 ctx->n_clients--;
96 memmove(&ctx->clients[i], &ctx->clients[i+1],
97 (ctx->n_clients - i) * sizeof(*ctx->clients));
98 ctx->clients = realloc(ctx->clients,
99 ctx->n_clients * sizeof(*ctx->clients));
100 }
101}
102
103static void rx_message(uint8_t eid, void *data, void *msg, size_t len)
104{
105 struct ctx *ctx = data;
106 struct iovec iov[2];
107 struct msghdr msghdr;
108 bool removed;
109 uint8_t type;
110 int i, rc;
111
112 if (len < 2)
113 return;
114
115 type = *(uint8_t *)msg;
116
117 if (ctx->verbose)
118 fprintf(stderr, "MCTP message received: len %zd, type %d\n",
119 len, type);
120
121 memset(&msghdr, 0, sizeof(msghdr));
122 msghdr.msg_iov = iov;
123 msghdr.msg_iovlen = 2;
124 iov[0].iov_base = &eid;
125 iov[0].iov_len = 1;
126 iov[1].iov_base = msg;
127 iov[1].iov_len = len;
128
129 for (i = 0; i < ctx->n_clients; i++) {
130 struct client *client = &ctx->clients[i];
131
132 if (client->type != type)
133 continue;
134
135 if (ctx->verbose)
136 fprintf(stderr, " forwarding to client %d\n", i);
137
138 rc = sendmsg(client->sock, &msghdr, 0);
139 if (rc != (ssize_t)(len + 1)) {
140 client->active = false;
141 removed = true;
142 }
143 }
144
145 if (removed)
146 client_remove_inactive(ctx);
147
148}
149
Jeremy Kerr34b9b3d2019-11-27 11:27:05 +0800150static int binding_null_init(struct mctp *mctp __unused,
151 struct binding *binding __unused,
152 mctp_eid_t eid __unused,
153 int n_params, char * const *params __unused)
154{
155 if (n_params != 0) {
156 warnx("null binding doesn't accept parameters");
157 return -1;
158 }
159 return 0;
160}
161
Jeremy Kerr8e31cfd2019-05-07 17:13:51 +0800162static int binding_serial_init(struct mctp *mctp, struct binding *binding,
163 mctp_eid_t eid, int n_params, char * const *params)
164{
165 struct mctp_binding_serial *serial;
166 const char *path;
167 int rc;
168
169 if (n_params != 1) {
170 warnx("serial binding requires device param");
171 return -1;
172 }
173
174 path = params[0];
175
176 serial = mctp_serial_init();
177 assert(serial);
178
179 rc = mctp_serial_open_path(serial, path);
180 if (rc)
181 return -1;
182
Jeremy Kerr3b36d172019-09-04 11:56:09 +0800183 mctp_register_bus(mctp, mctp_binding_serial_core(serial), eid);
Jeremy Kerr8e31cfd2019-05-07 17:13:51 +0800184
185 binding->data = serial;
186
187 return 0;
188}
189
190static int binding_serial_get_fd(struct binding *binding)
191{
192 return mctp_serial_get_fd(binding->data);
193}
194
195static int binding_serial_process(struct binding *binding)
196{
197 return mctp_serial_read(binding->data);
198}
199
200static int binding_astlpc_init(struct mctp *mctp, struct binding *binding,
201 mctp_eid_t eid, int n_params,
202 char * const *params __attribute__((unused)))
203{
204 struct mctp_binding_astlpc *astlpc;
205
206 if (n_params) {
207 warnx("astlpc binding does not accept parameters");
208 return -1;
209 }
210
Jeremy Kerrbc53d352019-08-28 14:26:14 +0530211 astlpc = mctp_astlpc_init_fileio();
Jeremy Kerr8e31cfd2019-05-07 17:13:51 +0800212 if (!astlpc) {
213 warnx("could not initialise astlpc binding");
214 return -1;
215 }
216
Jeremy Kerr3b36d172019-09-04 11:56:09 +0800217 mctp_register_bus(mctp, mctp_binding_astlpc_core(astlpc), eid);
218
Jeremy Kerr8e31cfd2019-05-07 17:13:51 +0800219 binding->data = astlpc;
220 return 0;
221}
222
223static int binding_astlpc_get_fd(struct binding *binding)
224{
225 return mctp_astlpc_get_fd(binding->data);
226}
227
228static int binding_astlpc_process(struct binding *binding)
229{
230 return mctp_astlpc_poll(binding->data);
231}
232
233struct binding bindings[] = {
234 {
Jeremy Kerr34b9b3d2019-11-27 11:27:05 +0800235 .name = "null",
236 .init = binding_null_init,
237 },
238 {
Jeremy Kerr8e31cfd2019-05-07 17:13:51 +0800239 .name = "serial",
240 .init = binding_serial_init,
241 .get_fd = binding_serial_get_fd,
242 .process = binding_serial_process,
243 },
244 {
245 .name = "astlpc",
246 .init = binding_astlpc_init,
247 .get_fd = binding_astlpc_get_fd,
248 .process = binding_astlpc_process,
249 }
250};
251
252struct binding *binding_lookup(const char *name)
253{
254 struct binding *binding;
255 unsigned int i;
256
257 for (i = 0; i < ARRAY_SIZE(bindings); i++) {
258 binding = &bindings[i];
259
260 if (!strcmp(binding->name, name))
261 return binding;
262 }
263
264 return NULL;
265}
266
267static int socket_init(struct ctx *ctx)
268{
269 struct sockaddr_un addr;
270 int namelen, rc;
271
272 namelen = sizeof(sockname) - 1;
273 addr.sun_family = AF_UNIX;
274 memcpy(addr.sun_path, sockname, namelen);
275
276 ctx->sock = socket(AF_UNIX, SOCK_SEQPACKET, 0);
277 if (ctx->sock < 0) {
278 warn("can't create socket");
279 return -1;
280 }
281
282 rc = bind(ctx->sock, (struct sockaddr *)&addr,
283 sizeof(addr.sun_family) + namelen);
284 if (rc) {
285 warn("can't bind socket");
286 goto err_close;
287 }
288
289 rc = listen(ctx->sock, 1);
290 if (rc) {
291 warn("can't listen on socket");
292 goto err_close;
293 }
294
295 return 0;
296
297err_close:
298 close(ctx->sock);
299 return -1;
300}
301
302static int socket_process(struct ctx *ctx)
303{
304 struct client *client;
305 int fd;
306
307 fd = accept4(ctx->sock, NULL, 0, SOCK_NONBLOCK);
308 if (fd < 0)
309 return -1;
310
311 ctx->n_clients++;
312 ctx->clients = realloc(ctx->clients,
313 ctx->n_clients * sizeof(struct client));
314
315 client = &ctx->clients[ctx->n_clients-1];
Andrew Jeffery8676c932020-01-24 12:22:21 +1030316 memset(client, 0, sizeof(*client));
Jeremy Kerr8e31cfd2019-05-07 17:13:51 +0800317 client->active = true;
318 client->sock = fd;
319
320 return 0;
321}
322
323static int client_process_recv(struct ctx *ctx, int idx)
324{
325 struct client *client = &ctx->clients[idx];
Jeremy Kerrf49b2ac2019-08-02 15:10:51 +0800326 uint8_t eid;
327 ssize_t len;
Jeremy Kerr8e31cfd2019-05-07 17:13:51 +0800328 int rc;
329
330 /* are we waiting for a type message? */
331 if (!client->type) {
332 uint8_t type;
333 rc = read(client->sock, &type, 1);
334 if (rc <= 0)
335 goto out_close;
336
337 if (type == 0) {
338 rc = -1;
339 goto out_close;
340 }
341 if (ctx->verbose)
342 fprintf(stderr, "client[%d] registered for type %u\n",
343 idx, type);
344 client->type = type;
345 return 0;
346 }
347
Jeremy Kerrf49b2ac2019-08-02 15:10:51 +0800348 len = recv(client->sock, NULL, 0, MSG_PEEK | MSG_TRUNC);
349 if (len < 0) {
Andrew Jeffery6896d412020-03-11 09:25:32 +1030350 if (errno != ECONNRESET)
351 warn("can't receive (peek) from client");
352
Andrew Jeffery13a40412020-02-07 17:05:20 +1030353 rc = -1;
Jeremy Kerrf49b2ac2019-08-02 15:10:51 +0800354 goto out_close;
355 }
356
Andrew Jefferyb93b6112020-06-05 14:13:44 +0930357 if ((size_t)len > ctx->buf_size) {
Jeremy Kerrf49b2ac2019-08-02 15:10:51 +0800358 void *tmp;
359
360 tmp = realloc(ctx->buf, len);
361 if (!tmp) {
362 warn("can't allocate for incoming message");
Andrew Jeffery13a40412020-02-07 17:05:20 +1030363 rc = -1;
Jeremy Kerrf49b2ac2019-08-02 15:10:51 +0800364 goto out_close;
365 }
366 ctx->buf = tmp;
367 ctx->buf_size = len;
368 }
369
370 rc = recv(client->sock, ctx->buf, ctx->buf_size, 0);
Jeremy Kerr8e31cfd2019-05-07 17:13:51 +0800371 if (rc < 0) {
Andrew Jeffery6896d412020-03-11 09:25:32 +1030372 if (errno != ECONNRESET)
373 warn("can't receive from client");
Andrew Jeffery13a40412020-02-07 17:05:20 +1030374 rc = -1;
Jeremy Kerr8e31cfd2019-05-07 17:13:51 +0800375 goto out_close;
376 }
377
Jeremy Kerr195a7c52019-06-24 14:24:56 +0800378 if (rc <= 0) {
Jeremy Kerr8e31cfd2019-05-07 17:13:51 +0800379 rc = -1;
380 goto out_close;
381 }
382
Jeremy Kerrf49b2ac2019-08-02 15:10:51 +0800383 eid = *(uint8_t *)ctx->buf;
Jeremy Kerrd690d8e2019-08-01 21:16:58 +0800384
Jeremy Kerr195a7c52019-06-24 14:24:56 +0800385 if (ctx->verbose)
386 fprintf(stderr,
387 "client[%d] sent message: dest 0x%02x len %d\n",
388 idx, eid, rc - 1);
Jeremy Kerr8e31cfd2019-05-07 17:13:51 +0800389
Jeremy Kerr195a7c52019-06-24 14:24:56 +0800390
391 if (eid == ctx->local_eid)
Jeremy Kerrf49b2ac2019-08-02 15:10:51 +0800392 rx_message(eid, ctx, ctx->buf + 1, rc - 1);
Jeremy Kerr195a7c52019-06-24 14:24:56 +0800393 else
Jeremy Kerrf49b2ac2019-08-02 15:10:51 +0800394 tx_message(ctx, eid, ctx->buf + 1, rc - 1);
Jeremy Kerr8e31cfd2019-05-07 17:13:51 +0800395
396 return 0;
397
398out_close:
399 client->active = false;
400 return rc;
401}
402
403static int binding_init(struct ctx *ctx, const char *name,
404 int argc, char * const *argv)
405{
406 int rc;
407
408 ctx->binding = binding_lookup(name);
409 if (!ctx->binding) {
410 warnx("no such binding '%s'", name);
411 return -1;
412 }
413
414 rc = ctx->binding->init(ctx->mctp, ctx->binding, ctx->local_eid,
415 argc, argv);
416 return rc;
417}
418
419enum {
420 FD_BINDING = 0,
421 FD_SOCKET,
Andrew Jeffery490e3872021-08-24 21:11:30 +0930422 FD_SIGNAL,
Jeremy Kerr8e31cfd2019-05-07 17:13:51 +0800423 FD_NR,
424};
425
426static int run_daemon(struct ctx *ctx)
427{
428 bool clients_changed = false;
Andrew Jeffery490e3872021-08-24 21:11:30 +0930429 sigset_t mask;
Jeremy Kerr8e31cfd2019-05-07 17:13:51 +0800430 int rc, i;
431
432 ctx->pollfds = malloc(FD_NR * sizeof(struct pollfd));
433
Jeremy Kerr34b9b3d2019-11-27 11:27:05 +0800434 if (ctx->binding->get_fd) {
435 ctx->pollfds[FD_BINDING].fd =
436 ctx->binding->get_fd(ctx->binding);
437 ctx->pollfds[FD_BINDING].events = POLLIN;
438 } else {
439 ctx->pollfds[FD_BINDING].fd = -1;
440 ctx->pollfds[FD_BINDING].events = 0;
441 }
Jeremy Kerr8e31cfd2019-05-07 17:13:51 +0800442
Andrew Jeffery490e3872021-08-24 21:11:30 +0930443 sigemptyset(&mask);
444 sigaddset(&mask, SIGINT);
445 sigaddset(&mask, SIGTERM);
446 sigaddset(&mask, SIGQUIT);
447
448 if ((rc = sigprocmask(SIG_BLOCK, &mask, NULL)) == -1) {
449 warn("sigprocmask");
450 return rc;
451 }
452
453 ctx->pollfds[FD_SIGNAL].fd = signalfd(-1, &mask, 0);
454 ctx->pollfds[FD_SIGNAL].events = POLLIN;
455
Jeremy Kerr8e31cfd2019-05-07 17:13:51 +0800456 ctx->pollfds[FD_SOCKET].fd = ctx->sock;
457 ctx->pollfds[FD_SOCKET].events = POLLIN;
458
459 mctp_set_rx_all(ctx->mctp, rx_message, ctx);
460
461 for (;;) {
462 if (clients_changed) {
463 int i;
464
465 ctx->pollfds = realloc(ctx->pollfds,
466 (ctx->n_clients + FD_NR) *
467 sizeof(struct pollfd));
468
469 for (i = 0; i < ctx->n_clients; i++) {
470 ctx->pollfds[FD_NR+i].fd =
471 ctx->clients[i].sock;
472 ctx->pollfds[FD_NR+i].events = POLLIN;
473 }
474 clients_changed = false;
475 }
476
477 rc = poll(ctx->pollfds, ctx->n_clients + FD_NR, -1);
478 if (rc < 0) {
479 warn("poll failed");
480 break;
481 }
482
483 if (!rc)
484 continue;
485
Andrew Jeffery490e3872021-08-24 21:11:30 +0930486 if (ctx->pollfds[FD_SIGNAL].revents) {
487 struct signalfd_siginfo si;
488 ssize_t got;
489
490 got = read(ctx->pollfds[FD_SIGNAL].fd, &si, sizeof(si));
491 if (got == sizeof(si)) {
492 warnx("Received %s, quitting",
493 strsignal(si.ssi_signo));
494 rc = 0;
495 break;
496 } else {
497 warnx("Unexpected read result for signalfd: %d",
498 rc);
499 warnx("Quitting on the basis that signalfd became ready");
500 rc = -1;
501 break;
502 }
503 }
504
Jeremy Kerr8e31cfd2019-05-07 17:13:51 +0800505 if (ctx->pollfds[FD_BINDING].revents) {
Jeremy Kerr34b9b3d2019-11-27 11:27:05 +0800506 rc = 0;
507 if (ctx->binding->process)
508 rc = ctx->binding->process(ctx->binding);
Jeremy Kerr8e31cfd2019-05-07 17:13:51 +0800509 if (rc)
510 break;
511 }
512
513 for (i = 0; i < ctx->n_clients; i++) {
514 if (!ctx->pollfds[FD_NR+i].revents)
515 continue;
516
517 rc = client_process_recv(ctx, i);
518 if (rc)
519 clients_changed = true;
520 }
521
522 if (ctx->pollfds[FD_SOCKET].revents) {
523 rc = socket_process(ctx);
524 if (rc)
525 break;
526 clients_changed = true;
527 }
528
529 if (clients_changed)
530 client_remove_inactive(ctx);
Jeremy Kerr8e31cfd2019-05-07 17:13:51 +0800531 }
532
533
534 free(ctx->pollfds);
535
536 return rc;
537}
538
539static const struct option options[] = {
540 { "verbose", no_argument, 0, 'v' },
541 { "eid", required_argument, 0, 'e' },
542 { 0 },
543};
544
545static void usage(const char *progname)
546{
547 unsigned int i;
548
549 fprintf(stderr, "usage: %s <binding> [params]\n", progname);
550 fprintf(stderr, "Available bindings:\n");
551 for (i = 0; i < ARRAY_SIZE(bindings); i++)
552 fprintf(stderr, " %s\n", bindings[i].name);
553}
554
555int main(int argc, char * const *argv)
556{
557 struct ctx *ctx, _ctx;
558 int rc;
559
560 ctx = &_ctx;
561 ctx->clients = NULL;
562 ctx->n_clients = 0;
563 ctx->local_eid = local_eid_default;
Andrew Jeffery04b81fc2020-02-05 13:07:29 +1030564 ctx->verbose = false;
Jeremy Kerr8e31cfd2019-05-07 17:13:51 +0800565
566 for (;;) {
567 rc = getopt_long(argc, argv, "e:v", options, NULL);
568 if (rc == -1)
569 break;
570 switch (rc) {
571 case 'v':
572 ctx->verbose = true;
573 break;
574 case 'e':
575 ctx->local_eid = atoi(optarg);
576 break;
577 default:
578 fprintf(stderr, "Invalid argument\n");
579 return EXIT_FAILURE;
580 }
581 }
582
583 if (optind >= argc) {
584 fprintf(stderr, "missing binding argument\n");
585 usage(argv[0]);
586 return EXIT_FAILURE;
587 }
588
Jeremy Kerrf49b2ac2019-08-02 15:10:51 +0800589 /* setup initial buffer */
590 ctx->buf_size = 4096;
591 ctx->buf = malloc(ctx->buf_size);
592
Jeremy Kerr0b278a62019-05-30 21:27:21 +0800593 mctp_set_log_stdio(ctx->verbose ? MCTP_LOG_DEBUG : MCTP_LOG_WARNING);
594
Jeremy Kerr8e31cfd2019-05-07 17:13:51 +0800595 ctx->mctp = mctp_init();
596 assert(ctx->mctp);
597
598 rc = binding_init(ctx, argv[optind], argc - optind - 1, argv + optind + 1);
599 if (rc)
600 return EXIT_FAILURE;
601
Andrew Jefferyd4103f82021-06-16 14:39:36 +0930602 rc = sd_listen_fds(true);
603 if (rc <= 0) {
604 rc = socket_init(ctx);
605 if (rc)
606 return EXIT_FAILURE;
607 } else {
608 ctx->sock = SD_LISTEN_FDS_START;
609 }
Jeremy Kerr8e31cfd2019-05-07 17:13:51 +0800610
611 rc = run_daemon(ctx);
612
613 return rc ? EXIT_FAILURE : EXIT_SUCCESS;
614
615}