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