blob: fc5d58348bbcb49daa2481252a7d39fe8e6ff146 [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
5#include <assert.h>
6#include <err.h>
Andrew Jeffery6896d412020-03-11 09:25:32 +10307#include <errno.h>
Jeremy Kerr8e31cfd2019-05-07 17:13:51 +08008#include <getopt.h>
Andrew Jefferyb93b6112020-06-05 14:13:44 +09309#include <limits.h>
Jeremy Kerr8e31cfd2019-05-07 17:13:51 +080010#include <poll.h>
Andrew Jeffery04b81fc2020-02-05 13:07:29 +103011#include <stdbool.h>
Jeremy Kerr8e31cfd2019-05-07 17:13:51 +080012#include <stdio.h>
13#include <stdlib.h>
14#include <string.h>
15#include <unistd.h>
16
17#include <sys/socket.h>
18#include <sys/un.h>
19
20#include "libmctp.h"
21#include "libmctp-serial.h"
22#include "libmctp-astlpc.h"
23
24#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
Jeremy Kerr34b9b3d2019-11-27 11:27:05 +080025#define __unused __attribute__((unused))
Jeremy Kerr8e31cfd2019-05-07 17:13:51 +080026
27static const mctp_eid_t local_eid_default = 8;
28static char sockname[] = "\0mctp-mux";
29
30struct binding {
31 const char *name;
32 int (*init)(struct mctp *mctp, struct binding *binding,
33 mctp_eid_t eid, int n_params,
34 char * const * params);
35 int (*get_fd)(struct binding *binding);
36 int (*process)(struct binding *binding);
37 void *data;
38};
39
40struct client {
41 bool active;
42 int sock;
43 uint8_t type;
44};
45
46struct ctx {
47 struct mctp *mctp;
48 struct binding *binding;
49 bool verbose;
50 int local_eid;
Jeremy Kerrf49b2ac2019-08-02 15:10:51 +080051 void *buf;
52 size_t buf_size;
Jeremy Kerr8e31cfd2019-05-07 17:13:51 +080053
54 int sock;
55 struct pollfd *pollfds;
56
57 struct client *clients;
58 int n_clients;
59};
60
61static void tx_message(struct ctx *ctx, mctp_eid_t eid, void *msg, size_t len)
62{
63 mctp_message_tx(ctx->mctp, eid, msg, len);
64}
65
66static void client_remove_inactive(struct ctx *ctx)
67{
68 int i;
69
70 for (i = 0; i < ctx->n_clients; i++) {
71 struct client *client = &ctx->clients[i];
72 if (client->active)
73 continue;
74 close(client->sock);
75
76 ctx->n_clients--;
77 memmove(&ctx->clients[i], &ctx->clients[i+1],
78 (ctx->n_clients - i) * sizeof(*ctx->clients));
79 ctx->clients = realloc(ctx->clients,
80 ctx->n_clients * sizeof(*ctx->clients));
81 }
82}
83
84static void rx_message(uint8_t eid, void *data, void *msg, size_t len)
85{
86 struct ctx *ctx = data;
87 struct iovec iov[2];
88 struct msghdr msghdr;
89 bool removed;
90 uint8_t type;
91 int i, rc;
92
93 if (len < 2)
94 return;
95
96 type = *(uint8_t *)msg;
97
98 if (ctx->verbose)
99 fprintf(stderr, "MCTP message received: len %zd, type %d\n",
100 len, type);
101
102 memset(&msghdr, 0, sizeof(msghdr));
103 msghdr.msg_iov = iov;
104 msghdr.msg_iovlen = 2;
105 iov[0].iov_base = &eid;
106 iov[0].iov_len = 1;
107 iov[1].iov_base = msg;
108 iov[1].iov_len = len;
109
110 for (i = 0; i < ctx->n_clients; i++) {
111 struct client *client = &ctx->clients[i];
112
113 if (client->type != type)
114 continue;
115
116 if (ctx->verbose)
117 fprintf(stderr, " forwarding to client %d\n", i);
118
119 rc = sendmsg(client->sock, &msghdr, 0);
120 if (rc != (ssize_t)(len + 1)) {
121 client->active = false;
122 removed = true;
123 }
124 }
125
126 if (removed)
127 client_remove_inactive(ctx);
128
129}
130
Jeremy Kerr34b9b3d2019-11-27 11:27:05 +0800131static int binding_null_init(struct mctp *mctp __unused,
132 struct binding *binding __unused,
133 mctp_eid_t eid __unused,
134 int n_params, char * const *params __unused)
135{
136 if (n_params != 0) {
137 warnx("null binding doesn't accept parameters");
138 return -1;
139 }
140 return 0;
141}
142
Jeremy Kerr8e31cfd2019-05-07 17:13:51 +0800143static int binding_serial_init(struct mctp *mctp, struct binding *binding,
144 mctp_eid_t eid, int n_params, char * const *params)
145{
146 struct mctp_binding_serial *serial;
147 const char *path;
148 int rc;
149
150 if (n_params != 1) {
151 warnx("serial binding requires device param");
152 return -1;
153 }
154
155 path = params[0];
156
157 serial = mctp_serial_init();
158 assert(serial);
159
160 rc = mctp_serial_open_path(serial, path);
161 if (rc)
162 return -1;
163
Jeremy Kerr3b36d172019-09-04 11:56:09 +0800164 mctp_register_bus(mctp, mctp_binding_serial_core(serial), eid);
Jeremy Kerr8e31cfd2019-05-07 17:13:51 +0800165
166 binding->data = serial;
167
168 return 0;
169}
170
171static int binding_serial_get_fd(struct binding *binding)
172{
173 return mctp_serial_get_fd(binding->data);
174}
175
176static int binding_serial_process(struct binding *binding)
177{
178 return mctp_serial_read(binding->data);
179}
180
181static int binding_astlpc_init(struct mctp *mctp, struct binding *binding,
182 mctp_eid_t eid, int n_params,
183 char * const *params __attribute__((unused)))
184{
185 struct mctp_binding_astlpc *astlpc;
186
187 if (n_params) {
188 warnx("astlpc binding does not accept parameters");
189 return -1;
190 }
191
Jeremy Kerrbc53d352019-08-28 14:26:14 +0530192 astlpc = mctp_astlpc_init_fileio();
Jeremy Kerr8e31cfd2019-05-07 17:13:51 +0800193 if (!astlpc) {
194 warnx("could not initialise astlpc binding");
195 return -1;
196 }
197
Jeremy Kerr3b36d172019-09-04 11:56:09 +0800198 mctp_register_bus(mctp, mctp_binding_astlpc_core(astlpc), eid);
199
Jeremy Kerr8e31cfd2019-05-07 17:13:51 +0800200 binding->data = astlpc;
201 return 0;
202}
203
204static int binding_astlpc_get_fd(struct binding *binding)
205{
206 return mctp_astlpc_get_fd(binding->data);
207}
208
209static int binding_astlpc_process(struct binding *binding)
210{
211 return mctp_astlpc_poll(binding->data);
212}
213
214struct binding bindings[] = {
215 {
Jeremy Kerr34b9b3d2019-11-27 11:27:05 +0800216 .name = "null",
217 .init = binding_null_init,
218 },
219 {
Jeremy Kerr8e31cfd2019-05-07 17:13:51 +0800220 .name = "serial",
221 .init = binding_serial_init,
222 .get_fd = binding_serial_get_fd,
223 .process = binding_serial_process,
224 },
225 {
226 .name = "astlpc",
227 .init = binding_astlpc_init,
228 .get_fd = binding_astlpc_get_fd,
229 .process = binding_astlpc_process,
230 }
231};
232
233struct binding *binding_lookup(const char *name)
234{
235 struct binding *binding;
236 unsigned int i;
237
238 for (i = 0; i < ARRAY_SIZE(bindings); i++) {
239 binding = &bindings[i];
240
241 if (!strcmp(binding->name, name))
242 return binding;
243 }
244
245 return NULL;
246}
247
248static int socket_init(struct ctx *ctx)
249{
250 struct sockaddr_un addr;
251 int namelen, rc;
252
253 namelen = sizeof(sockname) - 1;
254 addr.sun_family = AF_UNIX;
255 memcpy(addr.sun_path, sockname, namelen);
256
257 ctx->sock = socket(AF_UNIX, SOCK_SEQPACKET, 0);
258 if (ctx->sock < 0) {
259 warn("can't create socket");
260 return -1;
261 }
262
263 rc = bind(ctx->sock, (struct sockaddr *)&addr,
264 sizeof(addr.sun_family) + namelen);
265 if (rc) {
266 warn("can't bind socket");
267 goto err_close;
268 }
269
270 rc = listen(ctx->sock, 1);
271 if (rc) {
272 warn("can't listen on socket");
273 goto err_close;
274 }
275
276 return 0;
277
278err_close:
279 close(ctx->sock);
280 return -1;
281}
282
283static int socket_process(struct ctx *ctx)
284{
285 struct client *client;
286 int fd;
287
288 fd = accept4(ctx->sock, NULL, 0, SOCK_NONBLOCK);
289 if (fd < 0)
290 return -1;
291
292 ctx->n_clients++;
293 ctx->clients = realloc(ctx->clients,
294 ctx->n_clients * sizeof(struct client));
295
296 client = &ctx->clients[ctx->n_clients-1];
Andrew Jeffery8676c932020-01-24 12:22:21 +1030297 memset(client, 0, sizeof(*client));
Jeremy Kerr8e31cfd2019-05-07 17:13:51 +0800298 client->active = true;
299 client->sock = fd;
300
301 return 0;
302}
303
304static int client_process_recv(struct ctx *ctx, int idx)
305{
306 struct client *client = &ctx->clients[idx];
Jeremy Kerrf49b2ac2019-08-02 15:10:51 +0800307 uint8_t eid;
308 ssize_t len;
Jeremy Kerr8e31cfd2019-05-07 17:13:51 +0800309 int rc;
310
311 /* are we waiting for a type message? */
312 if (!client->type) {
313 uint8_t type;
314 rc = read(client->sock, &type, 1);
315 if (rc <= 0)
316 goto out_close;
317
318 if (type == 0) {
319 rc = -1;
320 goto out_close;
321 }
322 if (ctx->verbose)
323 fprintf(stderr, "client[%d] registered for type %u\n",
324 idx, type);
325 client->type = type;
326 return 0;
327 }
328
Jeremy Kerrf49b2ac2019-08-02 15:10:51 +0800329 len = recv(client->sock, NULL, 0, MSG_PEEK | MSG_TRUNC);
330 if (len < 0) {
Andrew Jeffery6896d412020-03-11 09:25:32 +1030331 if (errno != ECONNRESET)
332 warn("can't receive (peek) from client");
333
Andrew Jeffery13a40412020-02-07 17:05:20 +1030334 rc = -1;
Jeremy Kerrf49b2ac2019-08-02 15:10:51 +0800335 goto out_close;
336 }
337
Andrew Jefferyb93b6112020-06-05 14:13:44 +0930338 if ((size_t)len > ctx->buf_size) {
Jeremy Kerrf49b2ac2019-08-02 15:10:51 +0800339 void *tmp;
340
341 tmp = realloc(ctx->buf, len);
342 if (!tmp) {
343 warn("can't allocate for incoming message");
Andrew Jeffery13a40412020-02-07 17:05:20 +1030344 rc = -1;
Jeremy Kerrf49b2ac2019-08-02 15:10:51 +0800345 goto out_close;
346 }
347 ctx->buf = tmp;
348 ctx->buf_size = len;
349 }
350
351 rc = recv(client->sock, ctx->buf, ctx->buf_size, 0);
Jeremy Kerr8e31cfd2019-05-07 17:13:51 +0800352 if (rc < 0) {
Andrew Jeffery6896d412020-03-11 09:25:32 +1030353 if (errno != ECONNRESET)
354 warn("can't receive from client");
Andrew Jeffery13a40412020-02-07 17:05:20 +1030355 rc = -1;
Jeremy Kerr8e31cfd2019-05-07 17:13:51 +0800356 goto out_close;
357 }
358
Jeremy Kerr195a7c52019-06-24 14:24:56 +0800359 if (rc <= 0) {
Jeremy Kerr8e31cfd2019-05-07 17:13:51 +0800360 rc = -1;
361 goto out_close;
362 }
363
Jeremy Kerrf49b2ac2019-08-02 15:10:51 +0800364 eid = *(uint8_t *)ctx->buf;
Jeremy Kerrd690d8e2019-08-01 21:16:58 +0800365
Jeremy Kerr195a7c52019-06-24 14:24:56 +0800366 if (ctx->verbose)
367 fprintf(stderr,
368 "client[%d] sent message: dest 0x%02x len %d\n",
369 idx, eid, rc - 1);
Jeremy Kerr8e31cfd2019-05-07 17:13:51 +0800370
Jeremy Kerr195a7c52019-06-24 14:24:56 +0800371
372 if (eid == ctx->local_eid)
Jeremy Kerrf49b2ac2019-08-02 15:10:51 +0800373 rx_message(eid, ctx, ctx->buf + 1, rc - 1);
Jeremy Kerr195a7c52019-06-24 14:24:56 +0800374 else
Jeremy Kerrf49b2ac2019-08-02 15:10:51 +0800375 tx_message(ctx, eid, ctx->buf + 1, rc - 1);
Jeremy Kerr8e31cfd2019-05-07 17:13:51 +0800376
377 return 0;
378
379out_close:
380 client->active = false;
381 return rc;
382}
383
384static int binding_init(struct ctx *ctx, const char *name,
385 int argc, char * const *argv)
386{
387 int rc;
388
389 ctx->binding = binding_lookup(name);
390 if (!ctx->binding) {
391 warnx("no such binding '%s'", name);
392 return -1;
393 }
394
395 rc = ctx->binding->init(ctx->mctp, ctx->binding, ctx->local_eid,
396 argc, argv);
397 return rc;
398}
399
400enum {
401 FD_BINDING = 0,
402 FD_SOCKET,
403 FD_NR,
404};
405
406static int run_daemon(struct ctx *ctx)
407{
408 bool clients_changed = false;
409 int rc, i;
410
411 ctx->pollfds = malloc(FD_NR * sizeof(struct pollfd));
412
Jeremy Kerr34b9b3d2019-11-27 11:27:05 +0800413 if (ctx->binding->get_fd) {
414 ctx->pollfds[FD_BINDING].fd =
415 ctx->binding->get_fd(ctx->binding);
416 ctx->pollfds[FD_BINDING].events = POLLIN;
417 } else {
418 ctx->pollfds[FD_BINDING].fd = -1;
419 ctx->pollfds[FD_BINDING].events = 0;
420 }
Jeremy Kerr8e31cfd2019-05-07 17:13:51 +0800421
422 ctx->pollfds[FD_SOCKET].fd = ctx->sock;
423 ctx->pollfds[FD_SOCKET].events = POLLIN;
424
425 mctp_set_rx_all(ctx->mctp, rx_message, ctx);
426
427 for (;;) {
428 if (clients_changed) {
429 int i;
430
431 ctx->pollfds = realloc(ctx->pollfds,
432 (ctx->n_clients + FD_NR) *
433 sizeof(struct pollfd));
434
435 for (i = 0; i < ctx->n_clients; i++) {
436 ctx->pollfds[FD_NR+i].fd =
437 ctx->clients[i].sock;
438 ctx->pollfds[FD_NR+i].events = POLLIN;
439 }
440 clients_changed = false;
441 }
442
443 rc = poll(ctx->pollfds, ctx->n_clients + FD_NR, -1);
444 if (rc < 0) {
445 warn("poll failed");
446 break;
447 }
448
449 if (!rc)
450 continue;
451
452 if (ctx->pollfds[FD_BINDING].revents) {
Jeremy Kerr34b9b3d2019-11-27 11:27:05 +0800453 rc = 0;
454 if (ctx->binding->process)
455 rc = ctx->binding->process(ctx->binding);
Jeremy Kerr8e31cfd2019-05-07 17:13:51 +0800456 if (rc)
457 break;
458 }
459
460 for (i = 0; i < ctx->n_clients; i++) {
461 if (!ctx->pollfds[FD_NR+i].revents)
462 continue;
463
464 rc = client_process_recv(ctx, i);
465 if (rc)
466 clients_changed = true;
467 }
468
469 if (ctx->pollfds[FD_SOCKET].revents) {
470 rc = socket_process(ctx);
471 if (rc)
472 break;
473 clients_changed = true;
474 }
475
476 if (clients_changed)
477 client_remove_inactive(ctx);
478
479 }
480
481
482 free(ctx->pollfds);
483
484 return rc;
485}
486
487static const struct option options[] = {
488 { "verbose", no_argument, 0, 'v' },
489 { "eid", required_argument, 0, 'e' },
490 { 0 },
491};
492
493static void usage(const char *progname)
494{
495 unsigned int i;
496
497 fprintf(stderr, "usage: %s <binding> [params]\n", progname);
498 fprintf(stderr, "Available bindings:\n");
499 for (i = 0; i < ARRAY_SIZE(bindings); i++)
500 fprintf(stderr, " %s\n", bindings[i].name);
501}
502
503int main(int argc, char * const *argv)
504{
505 struct ctx *ctx, _ctx;
506 int rc;
507
508 ctx = &_ctx;
509 ctx->clients = NULL;
510 ctx->n_clients = 0;
511 ctx->local_eid = local_eid_default;
Andrew Jeffery04b81fc2020-02-05 13:07:29 +1030512 ctx->verbose = false;
Jeremy Kerr8e31cfd2019-05-07 17:13:51 +0800513
514 for (;;) {
515 rc = getopt_long(argc, argv, "e:v", options, NULL);
516 if (rc == -1)
517 break;
518 switch (rc) {
519 case 'v':
520 ctx->verbose = true;
521 break;
522 case 'e':
523 ctx->local_eid = atoi(optarg);
524 break;
525 default:
526 fprintf(stderr, "Invalid argument\n");
527 return EXIT_FAILURE;
528 }
529 }
530
531 if (optind >= argc) {
532 fprintf(stderr, "missing binding argument\n");
533 usage(argv[0]);
534 return EXIT_FAILURE;
535 }
536
Jeremy Kerrf49b2ac2019-08-02 15:10:51 +0800537 /* setup initial buffer */
538 ctx->buf_size = 4096;
539 ctx->buf = malloc(ctx->buf_size);
540
Jeremy Kerr0b278a62019-05-30 21:27:21 +0800541 mctp_set_log_stdio(ctx->verbose ? MCTP_LOG_DEBUG : MCTP_LOG_WARNING);
542
Jeremy Kerr8e31cfd2019-05-07 17:13:51 +0800543 ctx->mctp = mctp_init();
544 assert(ctx->mctp);
545
546 rc = binding_init(ctx, argv[optind], argc - optind - 1, argv + optind + 1);
547 if (rc)
548 return EXIT_FAILURE;
549
550 rc = socket_init(ctx);
551 if (rc)
552 return EXIT_FAILURE;
553
554 rc = run_daemon(ctx);
555
556 return rc ? EXIT_FAILURE : EXIT_SUCCESS;
557
558}