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