]> git.ipfire.org Git - thirdparty/bird.git/commitdiff
Flock: telnet to machines
authorMaria Matejka <mq@ucw.cz>
Fri, 27 Sep 2024 19:38:56 +0000 (21:38 +0200)
committerMaria Matejka <mq@ucw.cz>
Sun, 23 Feb 2025 18:07:35 +0000 (19:07 +0100)
flock/container.c
flock/ctl.c
flock/flock-cli
flock/flock.h
flock/hypervisor.c

index d3fe2e95dc2177b5a0407bb2476e9cc5da7e2504..b09f9cf6d914deb07b8016bc1c02009d188e7a1a 100644 (file)
@@ -8,6 +8,7 @@
 #include <poll.h>
 #include <sched.h>
 #include <stdlib.h>
+#include <sys/mount.h>
 #include <sys/wait.h>
 #include <unistd.h>
 
@@ -73,6 +74,39 @@ container_poweroff(int fd, int sig)
   exit(0);
 }
 
+static void
+container_zombie(void)
+{
+  zombie = 0;
+  log(L_INFO "Zombie elimination routine invoked.");
+  while (1) {
+    int status;
+    pid_t p = waitpid(-1, &status, WNOHANG);
+
+    if (p < 0)
+    {
+      if (errno != ECHILD)
+       log(L_ERR "Zombie elimination failed: %m");
+      return;
+    }
+
+    if (p == 0)
+      return;
+
+    const char *coreinfo = WCOREDUMP(status) ? " (core dumped)" : "";
+
+    if (WIFEXITED(status))
+      log(L_INFO "Process %d ended with status %d%s", p, WEXITSTATUS(status), coreinfo);
+    else if (WIFSIGNALED(status))
+      log(L_INFO "Process %d exited by signal %d (%s)%s", p, WTERMSIG(status), strsignal(WTERMSIG(status)), coreinfo);
+    else
+      log(L_ERR "Process %d exited with a strange status %d", p, status);
+  }
+}
+
+
+#define SYSCALL(x, ...)        ({ int e = x(__VA_ARGS__); if (e < 0) die("Failed to run %s at %s:%d: %m", #x, __FILE__, __LINE__); e; })
+
 static void
 container_mainloop(int fd)
 {
@@ -82,6 +116,10 @@ container_mainloop(int fd)
   signal(SIGINT, container_poweroff_sighandler);
   signal(SIGCHLD, container_child_sighandler);
 
+  /* Remounting proc to reflect the new PID namespace */
+  SYSCALL(mount, "none", "/", NULL, MS_REC | MS_PRIVATE, NULL);
+  SYSCALL(mount, "proc", "/proc", "proc", MS_NOSUID | MS_NODEV | MS_NOEXEC, NULL);
+
   /* TODO: mount overlayfs and chroot */
   while (1)
   {
@@ -95,13 +133,31 @@ container_mainloop(int fd)
 
     int res = ppoll(&pfd, 1, NULL, &newmask);
 
+    if ((res < 0) && (errno != EINTR))
+      log(L_INFO "ppoll returned -1: %m");
+
     if (poweroff)
       container_poweroff(fd, poweroff);
 
+    if (zombie)
+      container_zombie();
+
     if (pfd.revents & POLLIN)
     {
-      byte buf[128];
-      ssize_t sz = read(fd, buf, sizeof buf);
+      int sfd = -1;
+      byte buf[128], cbuf[CMSG_SPACE(sizeof sfd)];
+      struct iovec v = {
+       .iov_base = buf,
+       .iov_len = sizeof buf,
+      };
+      struct msghdr m = {
+       .msg_iov = &v,
+       .msg_iovlen = 1,
+       .msg_control = &cbuf,
+       .msg_controllen = sizeof cbuf,
+      };
+
+      int sz = recvmsg(fd, &m, 0);
       if (sz < 0)
       {
        log(L_ERR "error reading data from control socket: %m");
@@ -116,11 +172,47 @@ container_mainloop(int fd)
          container_poweroff(fd, 0);
          break;
 
+       case 0x21:
+         ASSERT_DIE(buf[2] == 0xf6);
+         struct cmsghdr *c = CMSG_FIRSTHDR(&m);
+         memcpy(&sfd, CMSG_DATA(c), sizeof sfd);
+
+         int e = fork();
+         if (e < 0) bug("Cannot fork: %m");
+         if (e == 0) {
+           int fd = accept(sfd, NULL, 0);
+           if (fd < 0)
+           {
+             log(L_ERR "failed to accept telnet connection: %m");
+             exit(1);
+           }
+           log(L_INFO "telnet connected");
+
+           close(0);
+           close(1);
+           close(2);
+
+           dup2(fd, 0);
+           dup2(fd, 1);
+
+           /* Unblock signals */
+           sigset_t newmask;
+           sigemptyset(&newmask);
+           sigprocmask(SIG_SETMASK, &newmask, NULL);
+
+           /* Exec the telnet */
+           e = execl("/usr/sbin/telnetd", "telnetd", "-E", "/bin/bash", NULL);
+           log(L_ERR "failed to execl: %m");
+           exit(42);
+         }
+         close(sfd);
+         break;
+
+       default:
+         log(L_ERR "unknown command on control socket: %d", buf[1]);
+         break;
       }
     }
-
-    /* TODO: check for telnet socket */
-    log(L_INFO "woken up, res %d (%m)!", res);
   }
 }
 
@@ -356,6 +448,14 @@ crt_err(sock *s, int err UNUSED)
   crt->ccc = NULL;
 }
 
+int
+container_ctl_fd(const char *name)
+{
+  uint h = mem_hash(name, strlen(name));
+  struct container_runtime *crt = HASH_FIND(hcf.hash, CRT, name, h);
+  return (crt && crt->s) ? crt->s->fd : -1;
+}
+
 static void
 container_created(callback *cb)
 {
index 7d816d58b8557fbc03be833a14e7f6efb88cabaf..a50fda7288639d1fed238d5c68962e1337f1655f 100644 (file)
@@ -186,15 +186,20 @@ hcs_parse(struct cbor_parser_context *ctx, const byte *buf, s64 size)
            break;
 
          case 3: /* telnet listener open */
-           if ((ctx->type != 7) || (ctx->value != 22))
-             CBOR_PARSER_ERROR("Expected null, got %u-%u", ctx->type, ctx->value);
-           /* TODO: allow this also for machines */
+           if ((ctx->type == 7) && (ctx->value == 22))
+           {
+             hexp_get_telnet(ctx->sock, NULL);
+             ctx->major_state = 1;
+             break;
+           }
 
-           log(L_INFO "Requested telnet open");
+           else if (ctx->type != 3)
+             CBOR_PARSER_ERROR("Expected null or string, got %u-%u", ctx->type, ctx->value);
 
-           hexp_get_telnet(ctx->sock, NULL);
+           ASSERT_DIE(!ctx->target_buf);
+           ctx->cfg.cf.name = ctx->target_buf = lp_alloc(ctx->lp, ctx->value + 1);
+           ctx->target_len = ctx->value;
 
-           ctx->major_state = 1;
            break;
 
          case 4: /* telnet listener close */
@@ -364,6 +369,11 @@ hcs_parse(struct cbor_parser_context *ctx, const byte *buf, s64 size)
        /* Read completely! */
        switch (ctx->major_state)
        {
+         case 3:
+           hexp_get_telnet(ctx->sock, ctx->cfg.cf.name);
+           ctx->major_state = 1;
+           break;
+
          case 5:
            /* Actually not this one */
            CBOR_PARSER_ERROR("NOT IMPLEMENTED YET");
index 5cac0e6ccdeb17dbc4041fa73e990ffecd83cf22..dcc57949e66dae6ded18bd1c573ff2969242a14a 100755 (executable)
@@ -105,6 +105,12 @@ def container_stop(hypervisor: str, name: str):
     for k,v in msg(hypervisor, { 4: { 0: name, }}).items():
         print(k,v)
 
+@handler
+def container_telnet(hypervisor: str, name: str):
+    for k,v in msg(hypervisor, { 1: name}).items():
+        assert(k == -2)
+        os.execlp("telnet", "telnet", "localhost", str(v))
+
 try:
     binname = sys.argv.pop(0)
 except Exception as e:
index 0601848f4cf88214bb611f286801b88e66e04a28..81c9ef3fd2c5edcb08c2f3710a528056240f8688 100644 (file)
@@ -48,6 +48,7 @@ union flock_machine_config {
 
 void hypervisor_container_request(sock *s, const char *name, const char *basedir, const char *workdir);
 void hypervisor_container_shutdown(sock *s, const char *name);
+int container_ctl_fd(const char *name);
 
 extern event reboot_event, poweroff_event;
 extern event_list shutdown_event_list;
index 50232e509792f9176c94f6743723200cc9c3ae7b..8717c0390c102c491e28db00b81464c5bd4ac30b 100644 (file)
@@ -127,6 +127,8 @@ static struct hypervisor_exposed {
   pool *p;
   sock *s;
   struct birdloop *loop;
+  const char *port_name;
+  sock *port_sreq;
 } he;
 
 /**
@@ -136,7 +138,6 @@ static struct hypervisor_exposed {
 static void hexp_received_telnet(void *);
 struct hexp_received_telnet {
   event e;
-  struct hexp_telnet_port *p;
   int fd;
   u16 port;
 };
@@ -165,6 +166,7 @@ hypervisor_telnet_connected(sock *sk, uint size UNUSED)
   {
     log(L_INFO "telnet connected");
     close(fd);
+    sk_close(sk);
     return 1;
   }
 
@@ -214,20 +216,12 @@ hypervisor_exposed_parent_rx(sock *sk, uint size UNUSED)
   u16 port = ntohs(*((u16 *) &buf[3]));
   log(L_INFO "RX %d bytes, fd %d, port %u", e, sfd, port);
 
-  sock *skl = sk_new(sk->pool);
-  skl->type = SK_MAGIC;
-  skl->rx_hook = hypervisor_telnet_connected;
-  skl->fd = sfd;
-  if (sk_open(skl, sk->loop) < 0)
-    bug("Telnet listener: sk_open failed");
-
   struct hexp_received_telnet *hrt = mb_allocz(he.p, sizeof *hrt);
   *hrt = (struct hexp_received_telnet) {
     .e = {
       .hook = hexp_received_telnet,
       .data = hrt,
     },
-    .p = sk->data,
     .port = port,
     .fd = sfd,
   };
@@ -413,141 +407,108 @@ hypervisor_exposed_fork(void)
  * Hypervisor's mapping between external ports and names
  */
 
-#define HEXP_TELNET_KEY(tp)    tp->name, tp->hash
-#define HEXP_TELNET_NEXT(tp)   tp->next
-#define HEXP_TELNET_EQ(a,h,b,i)        ((h) == (i)) && (!(a) && !(b) || !strcmp(a,b))
-#define HEXP_TELNET_FN(a,h)    h
-
-#define TLIST_PREFIX hexp_telnet_requestor
-#define TLIST_TYPE struct hexp_telnet_requestor
-#define TLIST_ITEM n
-struct hexp_telnet_requestor {
-  TLIST_DEFAULT_NODE;
-  sock *s;
-  struct cbor_parser_context *ctx;
-};
-
-#define TLIST_WANT_ADD_TAIL
-#include "lib/tlists.h"
-
 static void
 hexp_sock_err(sock *s, int err UNUSED)
 {
-  struct hexp_telnet_requestor *req = s->data;
-  s->data = req->ctx;
-
-  hexp_telnet_requestor_rem_node(hexp_telnet_requestor_enlisted(req), req);
+  ASSERT_DIE(s == he.port_sreq);
+  he.port_name = NULL;
+  he.port_sreq = NULL;
 }
 
-struct hexp_telnet_port {
-  struct hexp_telnet_port *next;
-  const char *name;
-  uint hash;
-  uint port;
-
-  TLIST_LIST(hexp_telnet_requestor) requestors;
-  int fd;
-};
-
-static struct hexp_telnet {
-  pool *pool;
-  HASH(struct hexp_telnet_port) port_hash;
-} hexp_telnet;
-
-static void
-hexp_init_telnet(void)
+void
+hexp_get_telnet(sock *s, const char *name)
 {
-  pool *p = rp_new(hcs_pool, hcs_pool->domain, "Hypervisor exposed telnets");
-  hexp_telnet.pool = p;
-  HASH_INIT(hexp_telnet.port_hash, p, 6);
-}
+  ASSERT_DIE(!he.port_name);
+  he.port_name = name ?: "";
+  he.port_sreq = s;
 
-static void
-hexp_have_telnet(sock *s, struct hexp_telnet_port *p)
-{
-  struct linpool *lp = lp_new(s->pool);
-  struct cbor_writer *cw = cbor_init(s->tbuf, s->tbsize, lp);
+  uint8_t buf[64];
+  linpool *lp = lp_new(s->pool);
+  struct cbor_writer *cw = cbor_init(buf, sizeof buf, lp);
   cbor_open_block_with_length(cw, 1);
-  cbor_add_int(cw, -2);
-  cbor_add_int(cw, p->port);
-  sk_send(s, cw->pt);
+  cbor_add_int(cw, 1);
+  cw->cbor[cw->pt++] = 0xf6;
+
+  int e = write(he.s->fd, buf, cw->pt);
+  if (e != cw->pt)
+    bug("write error handling not implemented, got %d (%m)", e);
+
   rfree(lp);
+
+  s->err_paused = hexp_sock_err;
+  sk_pause_rx(s->loop, s);
 }
 
-void
-hexp_get_telnet(sock *s, const char *name)
+static void hexp_received_telnet(void *_data)
 {
-  if (!hexp_telnet.pool)
-    hexp_init_telnet();
-
-  uint h = name ? mem_hash(name, strlen(name)) : 0;
-  struct hexp_telnet_port *p = HASH_FIND(hexp_telnet.port_hash, HEXP_TELNET, name, h);
-  if (p && p->port)
-    return hexp_have_telnet(s, p);
-  else if (!p)
+  struct hexp_received_telnet *hrt = _data;
+
+  ASSERT_DIE(he.port_name);
+  const char *name = he.port_name;
+  he.port_name = NULL;
+
+  sock *s = he.port_sreq;
+  he.port_sreq = NULL;
+
+  if (name[0])
   {
-    he.s->data = p = mb_alloc(hcs_pool, sizeof *p);
-    *p = (struct hexp_telnet_port) {
-      .name = name,
-      .hash = h,
-      .fd = -1,
-    };
-    HASH_INSERT(hexp_telnet.port_hash, HEXP_TELNET, p);
+    /* Transferring the received listening socket to the container */
+    int fd = container_ctl_fd(name);
 
-    uint8_t buf[64];
-    linpool *lp = lp_new(s->pool);
-    struct cbor_writer *cw = cbor_init(buf, sizeof buf, lp);
+    /* TODO: unduplicate this code */
+    byte outbuf[128];
+    linpool *lp = lp_new(hcs_pool);
+    struct cbor_writer *cw = cbor_init(outbuf, sizeof outbuf, lp);
     cbor_open_block_with_length(cw, 1);
-    cbor_add_int(cw, 1);
-    cw->cbor[cw->pt++] = 0xf6;
-
+    cbor_add_int(cw, -2);
+    write_item(cw, 7, 22);
     struct iovec v = {
-      .iov_base = buf,
+      .iov_base = outbuf,
       .iov_len = cw->pt,
     };
+    byte cbuf[CMSG_SPACE(sizeof hrt->fd)];
     struct msghdr m = {
       .msg_iov = &v,
       .msg_iovlen = 1,
+      .msg_control = &cbuf,
+      .msg_controllen = sizeof cbuf,
     };
+    struct cmsghdr *c = CMSG_FIRSTHDR(&m);
+    c->cmsg_level = SOL_SOCKET;
+    c->cmsg_type = SCM_RIGHTS;
+    c->cmsg_len = CMSG_LEN(sizeof hrt->fd);
+    memcpy(CMSG_DATA(c), &hrt->fd, sizeof hrt->fd);
 
-    int e = sendmsg(he.s->fd, &m, 0);
-    if (e != cw->pt)
-      bug("sendmsg error handling not implemented, got %d (%m)", e);
+    int e = sendmsg(fd, &m, 0);
+    if (e < 0)
+      log(L_ERR "Failed to send socket: %m");
 
-    rfree(lp);
+    close(hrt->fd);
+  }
+  else
+  {
+    /* Opening listener here */
+
+    sock *skl = sk_new(hcs_pool);
+    skl->type = SK_MAGIC;
+    skl->rx_hook = hypervisor_telnet_connected;
+    skl->data = skl;
+    skl->fd = hrt->fd;
+    if (sk_open(skl, hcs_loop) < 0)
+      bug("Telnet listener: sk_open failed");
   }
 
-  s->err_paused = hexp_sock_err;
-  sk_pause_rx(s->loop, s);
-
-  struct hexp_telnet_requestor *req = mb_allocz(hcs_pool, sizeof *req);
-  req->s = s;
-  req->ctx = s->data;
-  s->data = req;
-  hexp_telnet_requestor_add_tail(&p->requestors, req);
-}
-
-static void hexp_received_telnet(void *_data)
-{
-  struct hexp_received_telnet *hrt = _data;
-
-  ASSERT_DIE(!hrt->p->port);
-  hrt->p->port = hrt->port;
-  hrt->p->fd = hrt->fd;
-
-  byte outbuf[128];
-  linpool *lp = lp_new(hcs_pool);
-  struct cbor_writer *cw = cbor_init(outbuf, sizeof outbuf, lp);
-  cbor_open_block_with_length(cw, 1);
-  cbor_add_int(cw, -2);
-  cbor_add_int(cw, hrt->port);
-
-  WALK_TLIST_DELSAFE(hexp_telnet_requestor, r, &hrt->p->requestors)
+  if (s)
   {
-    sk_resume_rx(r->s->loop, r->s);
-    memcpy(r->s->tbuf, outbuf, cw->pt);
-    sk_send(r->s, cw->pt);
-    hexp_telnet_requestor_rem_node(&hrt->p->requestors, r);
+    linpool *lp = lp_new(hcs_pool);
+    struct cbor_writer *cw = cbor_init(s->tbuf, s->tbsize, lp);
+    cbor_open_block_with_length(cw, 1);
+    cbor_add_int(cw, -2);
+    cbor_add_int(cw, hrt->port);
+
+    sk_send(s, cw->pt);
+    sk_resume_rx(hcs_loop, s);
+    rfree(lp);
   }
 
   birdloop_enter(he.loop);