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