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