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