blob: 4467fe3aa9b7a4a8ef3d5edabff95b035781f115 [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 Jeffery04b81fc2020-02-05 13:07:29 +103013#include <stdbool.h>
Jeremy Kerr8e31cfd2019-05-07 17:13:51 +080014#include <stdio.h>
15#include <stdlib.h>
16#include <string.h>
17#include <unistd.h>
18
19#include <sys/socket.h>
20#include <sys/un.h>
21
Andrew Jefferyd4103f82021-06-16 14:39:36 +093022#define SD_LISTEN_FDS_START 3
23
Jeremy Kerr8e31cfd2019-05-07 17:13:51 +080024#include "libmctp.h"
25#include "libmctp-serial.h"
26#include "libmctp-astlpc.h"
27
28#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
Jeremy Kerr34b9b3d2019-11-27 11:27:05 +080029#define __unused __attribute__((unused))
Jeremy Kerr8e31cfd2019-05-07 17:13:51 +080030
Andrew Jefferyd4103f82021-06-16 14:39:36 +093031#if HAVE_SYSTEMD_SD_DAEMON_H
32#include <systemd/sd-daemon.h>
33#else
34static inline int sd_listen_fds(int i __unused)
35{
36 return -1;
37}
38#endif
39
Jeremy Kerr8e31cfd2019-05-07 17:13:51 +080040static const mctp_eid_t local_eid_default = 8;
41static char sockname[] = "\0mctp-mux";
42
43struct binding {
44 const char *name;
45 int (*init)(struct mctp *mctp, struct binding *binding,
46 mctp_eid_t eid, int n_params,
47 char * const * params);
48 int (*get_fd)(struct binding *binding);
49 int (*process)(struct binding *binding);
50 void *data;
51};
52
53struct client {
54 bool active;
55 int sock;
56 uint8_t type;
57};
58
59struct ctx {
60 struct mctp *mctp;
61 struct binding *binding;
62 bool verbose;
63 int local_eid;
Jeremy Kerrf49b2ac2019-08-02 15:10:51 +080064 void *buf;
65 size_t buf_size;
Jeremy Kerr8e31cfd2019-05-07 17:13:51 +080066
67 int sock;
68 struct pollfd *pollfds;
69
70 struct client *clients;
71 int n_clients;
72};
73
74static void tx_message(struct ctx *ctx, mctp_eid_t eid, void *msg, size_t len)
75{
Andrew Jeffery06735052021-01-27 23:50:55 +103076 int rc;
77
78 rc = mctp_message_tx(ctx->mctp, eid, msg, len);
79 if (rc)
80 warnx("Failed to send message: %d", rc);
Jeremy Kerr8e31cfd2019-05-07 17:13:51 +080081}
82
83static void client_remove_inactive(struct ctx *ctx)
84{
85 int i;
86
87 for (i = 0; i < ctx->n_clients; i++) {
88 struct client *client = &ctx->clients[i];
89 if (client->active)
90 continue;
91 close(client->sock);
92
93 ctx->n_clients--;
94 memmove(&ctx->clients[i], &ctx->clients[i+1],
95 (ctx->n_clients - i) * sizeof(*ctx->clients));
96 ctx->clients = realloc(ctx->clients,
97 ctx->n_clients * sizeof(*ctx->clients));
98 }
99}
100
101static void rx_message(uint8_t eid, void *data, void *msg, size_t len)
102{
103 struct ctx *ctx = data;
104 struct iovec iov[2];
105 struct msghdr msghdr;
106 bool removed;
107 uint8_t type;
108 int i, rc;
109
110 if (len < 2)
111 return;
112
113 type = *(uint8_t *)msg;
114
115 if (ctx->verbose)
116 fprintf(stderr, "MCTP message received: len %zd, type %d\n",
117 len, type);
118
119 memset(&msghdr, 0, sizeof(msghdr));
120 msghdr.msg_iov = iov;
121 msghdr.msg_iovlen = 2;
122 iov[0].iov_base = &eid;
123 iov[0].iov_len = 1;
124 iov[1].iov_base = msg;
125 iov[1].iov_len = len;
126
127 for (i = 0; i < ctx->n_clients; i++) {
128 struct client *client = &ctx->clients[i];
129
130 if (client->type != type)
131 continue;
132
133 if (ctx->verbose)
134 fprintf(stderr, " forwarding to client %d\n", i);
135
136 rc = sendmsg(client->sock, &msghdr, 0);
137 if (rc != (ssize_t)(len + 1)) {
138 client->active = false;
139 removed = true;
140 }
141 }
142
143 if (removed)
144 client_remove_inactive(ctx);
145
146}
147
Jeremy Kerr34b9b3d2019-11-27 11:27:05 +0800148static int binding_null_init(struct mctp *mctp __unused,
149 struct binding *binding __unused,
150 mctp_eid_t eid __unused,
151 int n_params, char * const *params __unused)
152{
153 if (n_params != 0) {
154 warnx("null binding doesn't accept parameters");
155 return -1;
156 }
157 return 0;
158}
159
Jeremy Kerr8e31cfd2019-05-07 17:13:51 +0800160static int binding_serial_init(struct mctp *mctp, struct binding *binding,
161 mctp_eid_t eid, int n_params, char * const *params)
162{
163 struct mctp_binding_serial *serial;
164 const char *path;
165 int rc;
166
167 if (n_params != 1) {
168 warnx("serial binding requires device param");
169 return -1;
170 }
171
172 path = params[0];
173
174 serial = mctp_serial_init();
175 assert(serial);
176
177 rc = mctp_serial_open_path(serial, path);
178 if (rc)
179 return -1;
180
Jeremy Kerr3b36d172019-09-04 11:56:09 +0800181 mctp_register_bus(mctp, mctp_binding_serial_core(serial), eid);
Jeremy Kerr8e31cfd2019-05-07 17:13:51 +0800182
183 binding->data = serial;
184
185 return 0;
186}
187
188static int binding_serial_get_fd(struct binding *binding)
189{
190 return mctp_serial_get_fd(binding->data);
191}
192
193static int binding_serial_process(struct binding *binding)
194{
195 return mctp_serial_read(binding->data);
196}
197
198static int binding_astlpc_init(struct mctp *mctp, struct binding *binding,
199 mctp_eid_t eid, int n_params,
200 char * const *params __attribute__((unused)))
201{
202 struct mctp_binding_astlpc *astlpc;
203
204 if (n_params) {
205 warnx("astlpc binding does not accept parameters");
206 return -1;
207 }
208
Jeremy Kerrbc53d352019-08-28 14:26:14 +0530209 astlpc = mctp_astlpc_init_fileio();
Jeremy Kerr8e31cfd2019-05-07 17:13:51 +0800210 if (!astlpc) {
211 warnx("could not initialise astlpc binding");
212 return -1;
213 }
214
Jeremy Kerr3b36d172019-09-04 11:56:09 +0800215 mctp_register_bus(mctp, mctp_binding_astlpc_core(astlpc), eid);
216
Jeremy Kerr8e31cfd2019-05-07 17:13:51 +0800217 binding->data = astlpc;
218 return 0;
219}
220
221static int binding_astlpc_get_fd(struct binding *binding)
222{
223 return mctp_astlpc_get_fd(binding->data);
224}
225
226static int binding_astlpc_process(struct binding *binding)
227{
228 return mctp_astlpc_poll(binding->data);
229}
230
231struct binding bindings[] = {
232 {
Jeremy Kerr34b9b3d2019-11-27 11:27:05 +0800233 .name = "null",
234 .init = binding_null_init,
235 },
236 {
Jeremy Kerr8e31cfd2019-05-07 17:13:51 +0800237 .name = "serial",
238 .init = binding_serial_init,
239 .get_fd = binding_serial_get_fd,
240 .process = binding_serial_process,
241 },
242 {
243 .name = "astlpc",
244 .init = binding_astlpc_init,
245 .get_fd = binding_astlpc_get_fd,
246 .process = binding_astlpc_process,
247 }
248};
249
250struct binding *binding_lookup(const char *name)
251{
252 struct binding *binding;
253 unsigned int i;
254
255 for (i = 0; i < ARRAY_SIZE(bindings); i++) {
256 binding = &bindings[i];
257
258 if (!strcmp(binding->name, name))
259 return binding;
260 }
261
262 return NULL;
263}
264
265static int socket_init(struct ctx *ctx)
266{
267 struct sockaddr_un addr;
268 int namelen, rc;
269
270 namelen = sizeof(sockname) - 1;
271 addr.sun_family = AF_UNIX;
272 memcpy(addr.sun_path, sockname, namelen);
273
274 ctx->sock = socket(AF_UNIX, SOCK_SEQPACKET, 0);
275 if (ctx->sock < 0) {
276 warn("can't create socket");
277 return -1;
278 }
279
280 rc = bind(ctx->sock, (struct sockaddr *)&addr,
281 sizeof(addr.sun_family) + namelen);
282 if (rc) {
283 warn("can't bind socket");
284 goto err_close;
285 }
286
287 rc = listen(ctx->sock, 1);
288 if (rc) {
289 warn("can't listen on socket");
290 goto err_close;
291 }
292
293 return 0;
294
295err_close:
296 close(ctx->sock);
297 return -1;
298}
299
300static int socket_process(struct ctx *ctx)
301{
302 struct client *client;
303 int fd;
304
305 fd = accept4(ctx->sock, NULL, 0, SOCK_NONBLOCK);
306 if (fd < 0)
307 return -1;
308
309 ctx->n_clients++;
310 ctx->clients = realloc(ctx->clients,
311 ctx->n_clients * sizeof(struct client));
312
313 client = &ctx->clients[ctx->n_clients-1];
Andrew Jeffery8676c932020-01-24 12:22:21 +1030314 memset(client, 0, sizeof(*client));
Jeremy Kerr8e31cfd2019-05-07 17:13:51 +0800315 client->active = true;
316 client->sock = fd;
317
318 return 0;
319}
320
321static int client_process_recv(struct ctx *ctx, int idx)
322{
323 struct client *client = &ctx->clients[idx];
Jeremy Kerrf49b2ac2019-08-02 15:10:51 +0800324 uint8_t eid;
325 ssize_t len;
Jeremy Kerr8e31cfd2019-05-07 17:13:51 +0800326 int rc;
327
328 /* are we waiting for a type message? */
329 if (!client->type) {
330 uint8_t type;
331 rc = read(client->sock, &type, 1);
332 if (rc <= 0)
333 goto out_close;
334
335 if (type == 0) {
336 rc = -1;
337 goto out_close;
338 }
339 if (ctx->verbose)
340 fprintf(stderr, "client[%d] registered for type %u\n",
341 idx, type);
342 client->type = type;
343 return 0;
344 }
345
Jeremy Kerrf49b2ac2019-08-02 15:10:51 +0800346 len = recv(client->sock, NULL, 0, MSG_PEEK | MSG_TRUNC);
347 if (len < 0) {
Andrew Jeffery6896d412020-03-11 09:25:32 +1030348 if (errno != ECONNRESET)
349 warn("can't receive (peek) from client");
350
Andrew Jeffery13a40412020-02-07 17:05:20 +1030351 rc = -1;
Jeremy Kerrf49b2ac2019-08-02 15:10:51 +0800352 goto out_close;
353 }
354
Andrew Jefferyb93b6112020-06-05 14:13:44 +0930355 if ((size_t)len > ctx->buf_size) {
Jeremy Kerrf49b2ac2019-08-02 15:10:51 +0800356 void *tmp;
357
358 tmp = realloc(ctx->buf, len);
359 if (!tmp) {
360 warn("can't allocate for incoming message");
Andrew Jeffery13a40412020-02-07 17:05:20 +1030361 rc = -1;
Jeremy Kerrf49b2ac2019-08-02 15:10:51 +0800362 goto out_close;
363 }
364 ctx->buf = tmp;
365 ctx->buf_size = len;
366 }
367
368 rc = recv(client->sock, ctx->buf, ctx->buf_size, 0);
Jeremy Kerr8e31cfd2019-05-07 17:13:51 +0800369 if (rc < 0) {
Andrew Jeffery6896d412020-03-11 09:25:32 +1030370 if (errno != ECONNRESET)
371 warn("can't receive from client");
Andrew Jeffery13a40412020-02-07 17:05:20 +1030372 rc = -1;
Jeremy Kerr8e31cfd2019-05-07 17:13:51 +0800373 goto out_close;
374 }
375
Jeremy Kerr195a7c52019-06-24 14:24:56 +0800376 if (rc <= 0) {
Jeremy Kerr8e31cfd2019-05-07 17:13:51 +0800377 rc = -1;
378 goto out_close;
379 }
380
Jeremy Kerrf49b2ac2019-08-02 15:10:51 +0800381 eid = *(uint8_t *)ctx->buf;
Jeremy Kerrd690d8e2019-08-01 21:16:58 +0800382
Jeremy Kerr195a7c52019-06-24 14:24:56 +0800383 if (ctx->verbose)
384 fprintf(stderr,
385 "client[%d] sent message: dest 0x%02x len %d\n",
386 idx, eid, rc - 1);
Jeremy Kerr8e31cfd2019-05-07 17:13:51 +0800387
Jeremy Kerr195a7c52019-06-24 14:24:56 +0800388
389 if (eid == ctx->local_eid)
Jeremy Kerrf49b2ac2019-08-02 15:10:51 +0800390 rx_message(eid, ctx, ctx->buf + 1, rc - 1);
Jeremy Kerr195a7c52019-06-24 14:24:56 +0800391 else
Jeremy Kerrf49b2ac2019-08-02 15:10:51 +0800392 tx_message(ctx, eid, ctx->buf + 1, rc - 1);
Jeremy Kerr8e31cfd2019-05-07 17:13:51 +0800393
394 return 0;
395
396out_close:
397 client->active = false;
398 return rc;
399}
400
401static int binding_init(struct ctx *ctx, const char *name,
402 int argc, char * const *argv)
403{
404 int rc;
405
406 ctx->binding = binding_lookup(name);
407 if (!ctx->binding) {
408 warnx("no such binding '%s'", name);
409 return -1;
410 }
411
412 rc = ctx->binding->init(ctx->mctp, ctx->binding, ctx->local_eid,
413 argc, argv);
414 return rc;
415}
416
417enum {
418 FD_BINDING = 0,
419 FD_SOCKET,
420 FD_NR,
421};
422
423static int run_daemon(struct ctx *ctx)
424{
425 bool clients_changed = false;
426 int rc, i;
427
428 ctx->pollfds = malloc(FD_NR * sizeof(struct pollfd));
429
Jeremy Kerr34b9b3d2019-11-27 11:27:05 +0800430 if (ctx->binding->get_fd) {
431 ctx->pollfds[FD_BINDING].fd =
432 ctx->binding->get_fd(ctx->binding);
433 ctx->pollfds[FD_BINDING].events = POLLIN;
434 } else {
435 ctx->pollfds[FD_BINDING].fd = -1;
436 ctx->pollfds[FD_BINDING].events = 0;
437 }
Jeremy Kerr8e31cfd2019-05-07 17:13:51 +0800438
439 ctx->pollfds[FD_SOCKET].fd = ctx->sock;
440 ctx->pollfds[FD_SOCKET].events = POLLIN;
441
442 mctp_set_rx_all(ctx->mctp, rx_message, ctx);
443
444 for (;;) {
445 if (clients_changed) {
446 int i;
447
448 ctx->pollfds = realloc(ctx->pollfds,
449 (ctx->n_clients + FD_NR) *
450 sizeof(struct pollfd));
451
452 for (i = 0; i < ctx->n_clients; i++) {
453 ctx->pollfds[FD_NR+i].fd =
454 ctx->clients[i].sock;
455 ctx->pollfds[FD_NR+i].events = POLLIN;
456 }
457 clients_changed = false;
458 }
459
460 rc = poll(ctx->pollfds, ctx->n_clients + FD_NR, -1);
461 if (rc < 0) {
462 warn("poll failed");
463 break;
464 }
465
466 if (!rc)
467 continue;
468
469 if (ctx->pollfds[FD_BINDING].revents) {
Jeremy Kerr34b9b3d2019-11-27 11:27:05 +0800470 rc = 0;
471 if (ctx->binding->process)
472 rc = ctx->binding->process(ctx->binding);
Jeremy Kerr8e31cfd2019-05-07 17:13:51 +0800473 if (rc)
474 break;
475 }
476
477 for (i = 0; i < ctx->n_clients; i++) {
478 if (!ctx->pollfds[FD_NR+i].revents)
479 continue;
480
481 rc = client_process_recv(ctx, i);
482 if (rc)
483 clients_changed = true;
484 }
485
486 if (ctx->pollfds[FD_SOCKET].revents) {
487 rc = socket_process(ctx);
488 if (rc)
489 break;
490 clients_changed = true;
491 }
492
493 if (clients_changed)
494 client_remove_inactive(ctx);
495
496 }
497
498
499 free(ctx->pollfds);
500
501 return rc;
502}
503
504static const struct option options[] = {
505 { "verbose", no_argument, 0, 'v' },
506 { "eid", required_argument, 0, 'e' },
507 { 0 },
508};
509
510static void usage(const char *progname)
511{
512 unsigned int i;
513
514 fprintf(stderr, "usage: %s <binding> [params]\n", progname);
515 fprintf(stderr, "Available bindings:\n");
516 for (i = 0; i < ARRAY_SIZE(bindings); i++)
517 fprintf(stderr, " %s\n", bindings[i].name);
518}
519
520int main(int argc, char * const *argv)
521{
522 struct ctx *ctx, _ctx;
523 int rc;
524
525 ctx = &_ctx;
526 ctx->clients = NULL;
527 ctx->n_clients = 0;
528 ctx->local_eid = local_eid_default;
Andrew Jeffery04b81fc2020-02-05 13:07:29 +1030529 ctx->verbose = false;
Jeremy Kerr8e31cfd2019-05-07 17:13:51 +0800530
531 for (;;) {
532 rc = getopt_long(argc, argv, "e:v", options, NULL);
533 if (rc == -1)
534 break;
535 switch (rc) {
536 case 'v':
537 ctx->verbose = true;
538 break;
539 case 'e':
540 ctx->local_eid = atoi(optarg);
541 break;
542 default:
543 fprintf(stderr, "Invalid argument\n");
544 return EXIT_FAILURE;
545 }
546 }
547
548 if (optind >= argc) {
549 fprintf(stderr, "missing binding argument\n");
550 usage(argv[0]);
551 return EXIT_FAILURE;
552 }
553
Jeremy Kerrf49b2ac2019-08-02 15:10:51 +0800554 /* setup initial buffer */
555 ctx->buf_size = 4096;
556 ctx->buf = malloc(ctx->buf_size);
557
Jeremy Kerr0b278a62019-05-30 21:27:21 +0800558 mctp_set_log_stdio(ctx->verbose ? MCTP_LOG_DEBUG : MCTP_LOG_WARNING);
559
Jeremy Kerr8e31cfd2019-05-07 17:13:51 +0800560 ctx->mctp = mctp_init();
561 assert(ctx->mctp);
562
563 rc = binding_init(ctx, argv[optind], argc - optind - 1, argv + optind + 1);
564 if (rc)
565 return EXIT_FAILURE;
566
Andrew Jefferyd4103f82021-06-16 14:39:36 +0930567 rc = sd_listen_fds(true);
568 if (rc <= 0) {
569 rc = socket_init(ctx);
570 if (rc)
571 return EXIT_FAILURE;
572 } else {
573 ctx->sock = SD_LISTEN_FDS_START;
574 }
Jeremy Kerr8e31cfd2019-05-07 17:13:51 +0800575
576 rc = run_daemon(ctx);
577
578 return rc ? EXIT_FAILURE : EXIT_SUCCESS;
579
580}