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