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