#include <poll.h>
#include <sched.h>
#include <stdlib.h>
+#include <sys/mount.h>
#include <sys/wait.h>
#include <unistd.h>
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)
{
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)
{
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");
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);
}
}
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)
{
pool *p;
sock *s;
struct birdloop *loop;
+ const char *port_name;
+ sock *port_sreq;
} he;
/**
static void hexp_received_telnet(void *);
struct hexp_received_telnet {
event e;
- struct hexp_telnet_port *p;
int fd;
u16 port;
};
{
log(L_INFO "telnet connected");
close(fd);
+ sk_close(sk);
return 1;
}
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,
};
* 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);