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