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