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