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