From 56aff8ef39424fd07d01fa8edd63a06623e734e1 Mon Sep 17 00:00:00 2001 From: Maria Matejka Date: Fri, 27 Sep 2024 21:38:56 +0200 Subject: [PATCH] Flock: telnet to machines --- flock/container.c | 110 ++++++++++++++++++++++-- flock/ctl.c | 22 +++-- flock/flock-cli | 6 ++ flock/flock.h | 1 + flock/hypervisor.c | 203 ++++++++++++++++++--------------------------- 5 files changed, 210 insertions(+), 132 deletions(-) diff --git a/flock/container.c b/flock/container.c index d3fe2e95..b09f9cf6 100644 --- a/flock/container.c +++ b/flock/container.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include @@ -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) { diff --git a/flock/ctl.c b/flock/ctl.c index 7d816d58..a50fda72 100644 --- a/flock/ctl.c +++ b/flock/ctl.c @@ -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"); diff --git a/flock/flock-cli b/flock/flock-cli index 5cac0e6c..dcc57949 100755 --- a/flock/flock-cli +++ b/flock/flock-cli @@ -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: diff --git a/flock/flock.h b/flock/flock.h index 0601848f..81c9ef3f 100644 --- a/flock/flock.h +++ b/flock/flock.h @@ -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; diff --git a/flock/hypervisor.c b/flock/hypervisor.c index 50232e50..8717c039 100644 --- a/flock/hypervisor.c +++ b/flock/hypervisor.c @@ -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); -} - -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) -{ - pool *p = rp_new(hcs_pool, hcs_pool->domain, "Hypervisor exposed telnets"); - hexp_telnet.pool = p; - HASH_INIT(hexp_telnet.port_hash, p, 6); -} - -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); - cbor_open_block_with_length(cw, 1); - cbor_add_int(cw, -2); - cbor_add_int(cw, p->port); - sk_send(s, cw->pt); - rfree(lp); + ASSERT_DIE(s == he.port_sreq); + he.port_name = NULL; + he.port_sreq = NULL; } void hexp_get_telnet(sock *s, const char *name) { - if (!hexp_telnet.pool) - hexp_init_telnet(); + ASSERT_DIE(!he.port_name); + he.port_name = name ?: ""; + he.port_sreq = s; - 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) - { - 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); + 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, 1); + cw->cbor[cw->pt++] = 0xf6; - 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, 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); - struct iovec v = { - .iov_base = buf, - .iov_len = cw->pt, - }; - struct msghdr m = { - .msg_iov = &v, - .msg_iovlen = 1, - }; - - int e = sendmsg(he.s->fd, &m, 0); - if (e != cw->pt) - bug("sendmsg error handling not implemented, got %d (%m)", e); - - rfree(lp); - } + rfree(lp); 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; + ASSERT_DIE(he.port_name); + const char *name = he.port_name; + he.port_name = NULL; - 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); + sock *s = he.port_sreq; + he.port_sreq = NULL; - WALK_TLIST_DELSAFE(hexp_telnet_requestor, r, &hrt->p->requestors) + if (name[0]) { - 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); + /* Transferring the received listening socket to the container */ + int fd = container_ctl_fd(name); + + /* 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, -2); + write_item(cw, 7, 22); + struct iovec v = { + .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(fd, &m, 0); + if (e < 0) + log(L_ERR "Failed to send socket: %m"); + + 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"); + } + + if (s) + { + 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);