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