ipc.c (21759B)
1 #define _GNU_SOURCE 2 #include <arpa/inet.h> 3 #include <errno.h> 4 #include <libgen.h> 5 #include <net/if.h> 6 #include <netdb.h> 7 #include <netinet/in.h> 8 #include <stdarg.h> 9 #include <stdio.h> 10 #include <stdlib.h> 11 #include <string.h> 12 #include <strings.h> 13 #include <sys/socket.h> 14 #include <sys/stat.h> 15 #include <sys/types.h> 16 #include <sys/un.h> 17 #include <unistd.h> 18 19 #include "finwo/resp.h" 20 #include "rxi/log.h" 21 22 #include "config/daemon.h" 23 #include "config/ifaces.h" 24 #include "config/ports.h" 25 #include "ipc.h" 26 #include "netlink/netlink.h" 27 #include "netlink/rtnl.h" 28 #include "util/auth.h" 29 30 #define RBUF_MAX (1024 * 1024) 31 32 struct listener { 33 int fd; 34 int is_unix; 35 char *path; // unix only, for unlink on shutdown 36 }; 37 38 struct ipc_conn { 39 int fd; 40 int is_unix; 41 int role; 42 uid_t uid; 43 44 char *rbuf; size_t rlen, rcap; 45 char *wbuf; size_t wlen, wcap, wsent; 46 47 int close_after_write; 48 struct ipc_conn *next; 49 }; 50 51 static struct listener listeners[IPC_MAX_LISTENERS]; 52 static int listener_n = 0; 53 static struct ipc_conn *conns = NULL; 54 static int conn_n = 0; 55 56 static int wbuf_append(struct ipc_conn *c, const char *data, size_t len) { 57 if (c->wlen + len > c->wcap) { 58 size_t cap = c->wcap ? c->wcap : 1024; 59 while (cap < c->wlen + len) cap *= 2; 60 char *g = realloc(c->wbuf, cap); 61 if (!g) return -1; 62 c->wbuf = g; 63 c->wcap = cap; 64 } 65 memcpy(c->wbuf + c->wlen, data, len); 66 c->wlen += len; 67 return 0; 68 } 69 70 static void reply_obj(struct ipc_conn *c, resp_object *o) { 71 if (!o) return; 72 char *buf = NULL; 73 size_t len = 0; 74 if (resp_serialize(o, &buf, &len) == 0) { 75 wbuf_append(c, buf, len); 76 free(buf); 77 } 78 resp_free(o); 79 } 80 81 static void reply_ok(struct ipc_conn *c) { 82 reply_obj(c, resp_simple_init("OK")); 83 } 84 85 static void reply_err(struct ipc_conn *c, const char *fmt, ...) { 86 char msg[512]; 87 va_list ap; 88 va_start(ap, fmt); 89 vsnprintf(msg, sizeof(msg), fmt, ap); 90 va_end(ap); 91 reply_obj(c, resp_error_init(msg)); 92 } 93 94 static int run_hook(const char *cmd) { 95 if (!cmd || !*cmd) return 0; 96 log_info("ipc: running hook: %s", cmd); 97 int rc = system(cmd); 98 if (rc != 0) { 99 log_warn("hook `%s` failed with %d", cmd, rc); 100 return -1; 101 } 102 return 0; 103 } 104 105 static int do_ifup(struct ipc_conn *c, const char *ifname) { 106 struct linkd_iface *iface = ifaces_find(ifname); 107 if (!iface) { 108 log_info("ipc: ifup %s (not in interfaces, just bringing link up)", ifname); 109 if (rtnl_link_up(ifname) != 0) { 110 reply_err(c, "ifup %s failed", ifname); 111 return -1; 112 } 113 return 0; 114 } 115 log_info("ipc: ifup %s", ifname); 116 if (iface->pre_up && run_hook(iface->pre_up) != 0) { 117 reply_err(c, "pre-up hook failed for %s", ifname); 118 return -1; 119 } 120 121 if (iface->vlan_id >= 0) { 122 const char *raw = iface->vlan_raw_device; 123 char tmp[IF_NAMESIZE]; 124 if (!raw) { 125 char *dot = strrchr(iface->name, '.'); 126 if (dot) { 127 size_t len = (size_t)(dot - iface->name); 128 if (len < sizeof(tmp)) { 129 memcpy(tmp, iface->name, len); 130 tmp[len] = '\0'; 131 raw = tmp; 132 } 133 } 134 } 135 if (raw) { 136 log_info("ipc: ifup %s vlan %d on %s", ifname, iface->vlan_id, raw); 137 rtnl_vlan_create(ifname, raw, iface->vlan_id); 138 } 139 } 140 141 // `ifup <vrf>` alone must work, not only a full apply. 142 if (iface_is_vrf(iface)) { 143 log_info("ipc: ifup %s vrf table %d", ifname, iface->vrf_table); 144 rtnl_vrf_create(ifname, (uint32_t)iface->vrf_table); 145 } 146 147 if (iface_is_bridge(iface)) { 148 log_info("ipc: ifup %s bridge (ports %s)", ifname, 149 iface->bridge_ports ? iface->bridge_ports : "(none)"); 150 rtnl_bridge_create(ifname); 151 if (iface->bridge_stp) { 152 bool on = !strcasecmp(iface->bridge_stp, "on") || !strcasecmp(iface->bridge_stp, "yes"); 153 rtnl_bridge_set_stp(ifname, on); 154 } 155 if (iface->bridge_vlan_aware >= 0) { 156 rtnl_bridge_set_vlan_aware(ifname, iface->bridge_vlan_aware); 157 } 158 if (iface->bridge_ports && strcasecmp(iface->bridge_ports, "none") != 0) { 159 char *ports = strdup(iface->bridge_ports); 160 char *tok = strtok(ports, " \t"); 161 while (tok) { 162 if (strcasecmp(tok, "none") == 0) { tok = strtok(NULL, " \t"); continue; } 163 log_info("ipc: ifup %s bridge add port %s", ifname, tok); 164 rtnl_bridge_add_port(ifname, tok); 165 rtnl_link_up(tok); 166 tok = strtok(NULL, " \t"); 167 } 168 free(ports); 169 } 170 } 171 172 // Enslave before addresses land: entering a VRF flushes them. 173 if (iface->vrf_master) { 174 rtnl_vrf_add_port(iface->vrf_master, ifname); 175 } 176 if (rtnl_link_up(ifname) != 0) { 177 reply_err(c, "ifup %s: link up failed", ifname); 178 return -1; 179 } 180 for (struct iface_addr *a = iface->addrs; a; a = a->next) { 181 log_info("ipc: ifup %s addr %s", ifname, a->address); 182 rtnl_addr_add(ifname, a->address, a->netmask); 183 } 184 if (iface->gateway) { 185 log_info("ipc: ifup %s gateway %s", ifname, iface->gateway); 186 rtnl_route_add_default(iface->gateway, ifname); 187 } 188 if (iface->mtu && rtnl_link_set_mtu(ifname, iface->mtu) != 0) { 189 reply_err(c, "ifup %s: mtu failed", ifname); 190 return -1; 191 } 192 if (iface->hwaddress && rtnl_link_set_hwaddr(ifname, iface->hwaddress) != 0) { 193 reply_err(c, "ifup %s: hwaddress failed", ifname); 194 return -1; 195 } 196 if (iface->post_up && run_hook(iface->post_up) != 0) { 197 reply_err(c, "post-up hook failed for %s", ifname); 198 return -1; 199 } 200 return 0; 201 } 202 203 static int do_ifdown(struct ipc_conn *c, const char *ifname) { 204 struct linkd_iface *iface = ifaces_find(ifname); 205 if (!iface) { 206 log_info("ipc: ifdown %s (not in interfaces, just bringing link down)", ifname); 207 if (rtnl_link_down(ifname) != 0) { 208 reply_err(c, "ifdown %s failed", ifname); 209 return -1; 210 } 211 return 0; 212 } 213 log_info("ipc: ifdown %s", ifname); 214 if (iface->pre_down && run_hook(iface->pre_down) != 0) { 215 reply_err(c, "pre-down hook failed for %s", ifname); 216 return -1; 217 } 218 if (rtnl_link_down(ifname) != 0) { 219 reply_err(c, "ifdown %s failed", ifname); 220 return -1; 221 } 222 if (iface->post_down && run_hook(iface->post_down) != 0) { 223 reply_err(c, "post-down hook failed for %s", ifname); 224 return -1; 225 } 226 return 0; 227 } 228 229 static int count_ifaces(void) { 230 int n = 0; 231 for (struct linkd_iface *c = ifaces_list(); c; c = c->next) n++; 232 return n; 233 } 234 235 static int do_ifreload(struct ipc_conn *c) { 236 log_info("ipc: ifreload start, old list has %d", count_ifaces()); 237 // TODO: real diff-apply; this frees and re-parses. 238 ifaces_free(); 239 ports_free(); 240 241 const struct linkd_daemon_cfg *dcfg = daemon_cfg(); 242 if (load_namespace("interfaces", dcfg->iface, dcfg->iface_n) < 0) { 243 reply_err(c, "ifreload: failed to reload interface configuration"); 244 return -1; 245 } 246 if (load_namespace("ports", dcfg->ports, dcfg->ports_n) < 0) { 247 reply_err(c, "ifreload: failed to reload port configuration"); 248 return -1; 249 } 250 log_info("ipc: ifreload done, new list has %d", count_ifaces()); 251 return 0; 252 } 253 254 // Flat key/value pairs (RESP2 map). Keys may repeat: several addresses. 255 static void iface_to_map(resp_object *m, const struct linkd_iface *i) { 256 char num[16]; 257 resp_array_append_bulk(m, "name"); 258 resp_array_append_bulk(m, i->name); 259 if (i->method) { 260 resp_array_append_bulk(m, "method"); 261 resp_array_append_bulk(m, i->method); 262 } 263 resp_array_append_bulk(m, "auto"); 264 resp_array_append_bulk(m, i->auto_flag ? "yes" : "no"); 265 for (struct iface_addr *a = i->addrs; a; a = a->next) { 266 resp_array_append_bulk(m, "address"); 267 resp_array_append_bulk(m, a->address); 268 if (a->netmask) { 269 resp_array_append_bulk(m, "netmask"); 270 resp_array_append_bulk(m, a->netmask); 271 } 272 } 273 if (i->gateway) { resp_array_append_bulk(m, "gateway"); resp_array_append_bulk(m, i->gateway); } 274 if (i->mtu) { snprintf(num, sizeof(num), "%d", i->mtu); 275 resp_array_append_bulk(m, "mtu"); resp_array_append_bulk(m, num); } 276 if (i->hwaddress) { resp_array_append_bulk(m, "hwaddress"); resp_array_append_bulk(m, i->hwaddress); } 277 if (i->vlan_raw_device) { resp_array_append_bulk(m, "vlan-raw-device"); resp_array_append_bulk(m, i->vlan_raw_device); } 278 if (i->vlan_id >= 0) { snprintf(num, sizeof(num), "%d", i->vlan_id); 279 resp_array_append_bulk(m, "vlan-id"); resp_array_append_bulk(m, num); } 280 if (i->bridge_ports) { resp_array_append_bulk(m, "bridge-ports"); resp_array_append_bulk(m, i->bridge_ports); } 281 if (i->bridge_stp) { resp_array_append_bulk(m, "bridge-stp"); resp_array_append_bulk(m, i->bridge_stp); } 282 if (i->bridge_vlan_aware >= 0) { 283 resp_array_append_bulk(m, "bridge-vlan-aware"); 284 resp_array_append_bulk(m, i->bridge_vlan_aware ? "yes" : "no"); 285 } 286 if (i->vrf_table >= 0) { snprintf(num, sizeof(num), "%d", i->vrf_table); 287 resp_array_append_bulk(m, "vrf-table"); resp_array_append_bulk(m, num); } 288 if (i->vrf_master) { resp_array_append_bulk(m, "vrf"); resp_array_append_bulk(m, i->vrf_master); } 289 if (i->pre_up) { resp_array_append_bulk(m, "pre-up"); resp_array_append_bulk(m, i->pre_up); } 290 if (i->post_up) { resp_array_append_bulk(m, "post-up"); resp_array_append_bulk(m, i->post_up); } 291 if (i->pre_down) { resp_array_append_bulk(m, "pre-down"); resp_array_append_bulk(m, i->pre_down); } 292 if (i->post_down) { resp_array_append_bulk(m, "post-down"); resp_array_append_bulk(m, i->post_down); } 293 } 294 295 static int do_ifquery(struct ipc_conn *c, const char *ifname) { 296 if (ifname) { 297 struct linkd_iface *i = ifaces_find(ifname); 298 if (!i) { 299 reply_err(c, "no such interface: %s", ifname); 300 return -1; 301 } 302 resp_object *m = resp_array_init(); 303 iface_to_map(m, i); 304 reply_obj(c, m); 305 return 0; 306 } 307 308 resp_object *outer = resp_array_init(); 309 for (struct linkd_iface *i = ifaces_list(); i; i = i->next) { 310 resp_object *m = resp_array_init(); 311 iface_to_map(m, i); 312 resp_array_append_obj(outer, m); 313 } 314 reply_obj(c, outer); 315 return 0; 316 } 317 318 struct command { 319 const char *name; 320 int min_args; // including the verb 321 int max_args; // -1 = unlimited 322 int role; // minimum role 323 }; 324 325 static const struct command commands[] = { 326 { "PING", 1, 2, IPC_ROLE_NONE }, 327 { "INFO", 1, 1, IPC_ROLE_READONLY }, 328 { "COMMAND", 1, 2, IPC_ROLE_READONLY }, 329 { "IFQUERY", 1, 2, IPC_ROLE_READONLY }, 330 { "IFUP", 2, 2, IPC_ROLE_FULL }, 331 { "IFDOWN", 2, 2, IPC_ROLE_FULL }, 332 { "IFRELOAD", 1, 1, IPC_ROLE_FULL }, 333 { "AUTH", 2, 3, IPC_ROLE_NONE }, 334 { "QUIT", 1, 1, IPC_ROLE_NONE }, 335 }; 336 static const size_t commands_n = sizeof(commands) / sizeof(commands[0]); 337 338 static const char *arg(const resp_object *cmd, size_t i) { 339 if (!cmd || cmd->type != RESPT_ARRAY || i >= cmd->u.arr.n) return NULL; 340 const resp_object *e = &cmd->u.arr.elem[i]; 341 if (e->type != RESPT_BULK && e->type != RESPT_SIMPLE) return NULL; 342 return e->u.s; 343 } 344 345 static void do_info(struct ipc_conn *c) { 346 char buf[512]; 347 snprintf(buf, sizeof(buf), 348 "# Server\r\n" 349 "name:linkd\r\n" 350 "version:" __TARGET "\r\n" 351 "\r\n" 352 "# Clients\r\n" 353 "connected_clients:%d\r\n" 354 "\r\n" 355 "# Config\r\n" 356 "interfaces:%d\r\n", 357 conn_n, count_ifaces()); 358 resp_object *o = calloc(1, sizeof(*o)); 359 if (!o) return; 360 o->type = RESPT_BULK; 361 o->u.s = strdup(buf); 362 reply_obj(c, o); 363 } 364 365 static void do_command_list(struct ipc_conn *c) { 366 resp_object *a = resp_array_init(); 367 for (size_t i = 0; i < commands_n; i++) { 368 resp_object *e = resp_array_init(); 369 resp_array_append_bulk(e, commands[i].name); 370 resp_array_append_int(e, commands[i].max_args < 0 371 ? -commands[i].min_args : commands[i].max_args); 372 resp_array_append_obj(a, e); 373 } 374 reply_obj(c, a); 375 } 376 377 static void do_auth(struct ipc_conn *c, const resp_object *cmd) { 378 const struct linkd_daemon_cfg *cfg = daemon_cfg(); 379 if (!cfg->authfile) { 380 reply_err(c, "Client sent AUTH, but no password file is configured"); 381 return; 382 } 383 384 const char *user = arg(cmd, 1); 385 const char *pass = arg(cmd, 2); 386 if (!pass) { pass = user; user = "default"; } 387 if (!user || !pass) { 388 reply_err(c, "wrong number of arguments for 'AUTH'"); 389 return; 390 } 391 392 int role = auth_check(cfg->authfile, user, pass); 393 if (role == AUTH_ROLE_NONE) { 394 log_warn("ipc: failed AUTH for user '%s'", user); 395 reply_err(c, "WRONGPASS invalid username-password pair"); 396 return; 397 } 398 399 c->role = (role == AUTH_ROLE_FULL) ? IPC_ROLE_FULL : IPC_ROLE_READONLY; 400 log_info("ipc: user '%s' authenticated (%s)", user, 401 c->role == IPC_ROLE_FULL ? "full" : "readonly"); 402 reply_ok(c); 403 } 404 405 static void dispatch(struct ipc_conn *c, const resp_object *cmd) { 406 const char *verb = arg(cmd, 0); 407 if (!verb) { 408 reply_err(c, "ERR malformed command"); 409 return; 410 } 411 412 size_t argc = (cmd->type == RESPT_ARRAY) ? cmd->u.arr.n : 0; 413 414 const struct command *spec = NULL; 415 for (size_t i = 0; i < commands_n; i++) { 416 if (!strcasecmp(verb, commands[i].name)) { spec = &commands[i]; break; } 417 } 418 if (!spec) { 419 reply_err(c, "unknown command '%s'", verb); 420 return; 421 } 422 423 if ((int)argc < spec->min_args || 424 (spec->max_args >= 0 && (int)argc > spec->max_args)) { 425 reply_err(c, "wrong number of arguments for '%s'", verb); 426 return; 427 } 428 429 if (c->role < spec->role) { 430 reply_err(c, c->role == IPC_ROLE_NONE 431 ? "NOAUTH Authentication required" 432 : "NOPERM this command requires full access"); 433 return; 434 } 435 436 if (!strcasecmp(verb, "PING")) { 437 const char *msg = arg(cmd, 1); 438 if (msg) { 439 resp_object *o = calloc(1, sizeof(*o)); 440 if (o) { o->type = RESPT_BULK; o->u.s = strdup(msg); reply_obj(c, o); } 441 } else { 442 reply_obj(c, resp_simple_init("PONG")); 443 } 444 } else if (!strcasecmp(verb, "QUIT")) { 445 reply_ok(c); 446 c->close_after_write = 1; 447 } else if (!strcasecmp(verb, "INFO")) { 448 do_info(c); 449 } else if (!strcasecmp(verb, "COMMAND")) { 450 do_command_list(c); 451 } else if (!strcasecmp(verb, "AUTH")) { 452 do_auth(c, cmd); 453 } else if (!strcasecmp(verb, "IFQUERY")) { 454 do_ifquery(c, arg(cmd, 1)); 455 } else if (!strcasecmp(verb, "IFUP")) { 456 if (do_ifup(c, arg(cmd, 1)) == 0) reply_ok(c); 457 } else if (!strcasecmp(verb, "IFDOWN")) { 458 if (do_ifdown(c, arg(cmd, 1)) == 0) reply_ok(c); 459 } else if (!strcasecmp(verb, "IFRELOAD")) { 460 if (do_ifreload(c) == 0) reply_ok(c); 461 } 462 } 463 464 static void conn_close(struct ipc_conn *c) { 465 struct ipc_conn **p = &conns; 466 while (*p && *p != c) p = &(*p)->next; 467 if (*p) *p = c->next; 468 if (c->fd >= 0) close(c->fd); 469 free(c->rbuf); 470 free(c->wbuf); 471 free(c); 472 conn_n--; 473 } 474 475 static void conn_accept(struct listener *l) { 476 int fd = accept4(l->fd, NULL, NULL, SOCK_NONBLOCK | SOCK_CLOEXEC); 477 if (fd < 0) return; 478 479 if (conn_n >= IPC_MAX_CONNS) { 480 log_warn("ipc: connection limit reached, rejecting"); 481 close(fd); 482 return; 483 } 484 485 struct ipc_conn *c = calloc(1, sizeof(*c)); 486 if (!c) { close(fd); return; } 487 c->fd = fd; 488 c->is_unix = l->is_unix; 489 c->role = IPC_ROLE_NONE; 490 491 // Root over unix is pre-authenticated; the kernel vouches for the uid. 492 if (l->is_unix) { 493 struct ucred cred; 494 socklen_t len = sizeof(cred); 495 if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &cred, &len) == 0) { 496 c->uid = cred.uid; 497 if (cred.uid == 0) c->role = IPC_ROLE_FULL; 498 } 499 } 500 501 c->next = conns; 502 conns = c; 503 conn_n++; 504 } 505 506 static void conn_read(struct ipc_conn *c) { 507 char buf[4096]; 508 for (;;) { 509 ssize_t n = read(c->fd, buf, sizeof(buf)); 510 if (n == 0) { conn_close(c); return; } 511 if (n < 0) { 512 if (errno == EINTR) continue; 513 if (errno == EAGAIN || errno == EWOULDBLOCK) break; 514 conn_close(c); 515 return; 516 } 517 if (c->rlen + (size_t)n > RBUF_MAX) { 518 log_warn("ipc: request too large, dropping client"); 519 conn_close(c); 520 return; 521 } 522 if (c->rlen + (size_t)n > c->rcap) { 523 size_t cap = c->rcap ? c->rcap : 4096; 524 while (cap < c->rlen + (size_t)n) cap *= 2; 525 char *g = realloc(c->rbuf, cap); 526 if (!g) { conn_close(c); return; } 527 c->rbuf = g; 528 c->rcap = cap; 529 } 530 memcpy(c->rbuf + c->rlen, buf, (size_t)n); 531 c->rlen += (size_t)n; 532 if ((size_t)n < sizeof(buf)) break; 533 } 534 535 // Drain every complete command; this is what makes pipelining work. 536 for (;;) { 537 resp_object *cmd = NULL; 538 int used = resp_read_buf(c->rbuf, c->rlen, &cmd); 539 if (used <= 0) { 540 // 0 = nothing yet, <0 = partial; wait for more data. 541 if (cmd) resp_free(cmd); 542 break; 543 } 544 dispatch(c, cmd); 545 resp_free(cmd); 546 547 memmove(c->rbuf, c->rbuf + used, c->rlen - (size_t)used); 548 c->rlen -= (size_t)used; 549 if (c->rlen == 0) break; 550 } 551 } 552 553 static void conn_write(struct ipc_conn *c) { 554 while (c->wsent < c->wlen) { 555 ssize_t n = write(c->fd, c->wbuf + c->wsent, c->wlen - c->wsent); 556 if (n < 0) { 557 if (errno == EINTR) continue; 558 if (errno == EAGAIN || errno == EWOULDBLOCK) return; 559 conn_close(c); 560 return; 561 } 562 c->wsent += (size_t)n; 563 } 564 c->wlen = c->wsent = 0; 565 if (c->close_after_write) conn_close(c); 566 } 567 568 static int listen_unix(const char *path) { 569 struct sockaddr_un addr; 570 571 { 572 char *d = strdup(path); 573 if (d) { mkdir(dirname(d), 0755); free(d); } 574 } 575 unlink(path); 576 577 int fd = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0); 578 if (fd < 0) { 579 log_error("ipc: socket: %s", strerror(errno)); 580 return -1; 581 } 582 583 memset(&addr, 0, sizeof(addr)); 584 addr.sun_family = AF_UNIX; 585 strncpy(addr.sun_path, path, sizeof(addr.sun_path) - 1); 586 587 if (bind(fd, (struct sockaddr *)&addr, sizeof(addr)) != 0) { 588 log_error("ipc: bind %s: %s", path, strerror(errno)); 589 close(fd); 590 return -1; 591 } 592 if (chmod(path, 0600) != 0) { 593 log_warn("ipc: chmod %s: %s", path, strerror(errno)); 594 } 595 if (listen(fd, 8) != 0) { 596 log_error("ipc: listen %s: %s", path, strerror(errno)); 597 close(fd); 598 unlink(path); 599 return -1; 600 } 601 return fd; 602 } 603 604 static int listen_tcp(const char *hostport) { 605 char *copy = strdup(hostport); 606 if (!copy) return -1; 607 608 char *host = copy; 609 char *port = strrchr(copy, ':'); 610 if (port) { *port++ = '\0'; } else { port = (char *)"6789"; } 611 if (*host == '\0') host = NULL; 612 613 struct addrinfo hints = { 614 .ai_family = AF_UNSPEC, 615 .ai_socktype = SOCK_STREAM, 616 .ai_flags = AI_PASSIVE, 617 }; 618 struct addrinfo *res = NULL; 619 int gai = getaddrinfo(host, port, &hints, &res); 620 if (gai != 0) { 621 log_error("ipc: resolve %s: %s", hostport, gai_strerror(gai)); 622 free(copy); 623 return -1; 624 } 625 626 int fd = -1; 627 for (struct addrinfo *ai = res; ai; ai = ai->ai_next) { 628 fd = socket(ai->ai_family, ai->ai_socktype | SOCK_CLOEXEC | SOCK_NONBLOCK, ai->ai_protocol); 629 if (fd < 0) continue; 630 int one = 1; 631 setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)); 632 if (bind(fd, ai->ai_addr, ai->ai_addrlen) == 0 && listen(fd, 8) == 0) break; 633 close(fd); 634 fd = -1; 635 } 636 freeaddrinfo(res); 637 free(copy); 638 639 if (fd < 0) log_error("ipc: cannot listen on %s", hostport); 640 return fd; 641 } 642 643 int ipc_init(void) { 644 const struct linkd_daemon_cfg *cfg = daemon_cfg(); 645 int ok = 0; 646 647 for (size_t i = 0; i < cfg->listen_n && listener_n < IPC_MAX_LISTENERS; i++) { 648 const char *a = cfg->listen[i]; 649 int is_unix = !strncmp(a, "unix://", 7); 650 int fd; 651 652 if (is_unix) { 653 fd = listen_unix(a + 7); 654 } else { 655 // No way to authorise a tcp client without one; SO_PEERCRED is 656 // unix-only. 657 if (!cfg->authfile) { 658 log_error("ipc: %s needs `authfile` configured; refusing to listen", a); 659 continue; 660 } 661 fd = listen_tcp(a + 6); 662 } 663 if (fd < 0) continue; 664 665 listeners[listener_n].fd = fd; 666 listeners[listener_n].is_unix = is_unix; 667 listeners[listener_n].path = is_unix ? strdup(a + 7) : NULL; 668 listener_n++; 669 ok++; 670 log_info("ipc: listening on %s", a); 671 } 672 673 return ok ? 0 : -1; 674 } 675 676 void ipc_fini(void) { 677 while (conns) conn_close(conns); 678 for (int i = 0; i < listener_n; i++) { 679 if (listeners[i].fd >= 0) close(listeners[i].fd); 680 if (listeners[i].path) { 681 unlink(listeners[i].path); 682 free(listeners[i].path); 683 } 684 } 685 listener_n = 0; 686 } 687 688 int ipc_pollfds(struct pollfd *pfds, int max) { 689 int n = 0; 690 for (int i = 0; i < listener_n && n < max; i++) { 691 pfds[n].fd = listeners[i].fd; 692 pfds[n].events = POLLIN; 693 pfds[n].revents = 0; 694 n++; 695 } 696 for (struct ipc_conn *c = conns; c && n < max; c = c->next) { 697 pfds[n].fd = c->fd; 698 pfds[n].events = POLLIN | (c->wsent < c->wlen ? POLLOUT : 0); 699 pfds[n].revents = 0; 700 n++; 701 } 702 return n; 703 } 704 705 void ipc_dispatch(struct pollfd *pfds, int n) { 706 for (int i = 0; i < n; i++) { 707 if (!pfds[i].revents) continue; 708 709 int is_listener = 0; 710 for (int l = 0; l < listener_n; l++) { 711 if (listeners[l].fd == pfds[i].fd) { 712 conn_accept(&listeners[l]); 713 is_listener = 1; 714 break; 715 } 716 } 717 if (is_listener) continue; 718 719 // Look up by fd: an earlier fd may have closed and freed this one. 720 struct ipc_conn *c = conns; 721 while (c && c->fd != pfds[i].fd) c = c->next; 722 if (!c) continue; 723 724 if (pfds[i].revents & (POLLERR | POLLHUP | POLLNVAL)) { conn_close(c); continue; } 725 if (pfds[i].revents & POLLIN) conn_read(c); 726 727 // conn_read may have closed it; re-find before writing. 728 struct ipc_conn *still = conns; 729 while (still && still != c) still = still->next; 730 if (!still) continue; 731 732 if (c->wsent < c->wlen) conn_write(c); 733 } 734 }