mirror of
https://gitlab.nic.cz/labs/bird.git
synced 2024-12-22 09:41:54 +00:00
Flock: telnet to machines
This commit is contained in:
parent
d36ad6af6a
commit
56aff8ef39
@ -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);
|
||||
|
||||
/* TODO: check for telnet socket */
|
||||
log(L_INFO "woken up, res %d (%m)!", res);
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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)
|
||||
{
|
||||
|
24
flock/ctl.c
24
flock/ctl.c
@ -186,16 +186,21 @@ 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 */
|
||||
|
||||
log(L_INFO "Requested telnet open");
|
||||
|
||||
if ((ctx->type == 7) && (ctx->value == 22))
|
||||
{
|
||||
hexp_get_telnet(ctx->sock, NULL);
|
||||
|
||||
ctx->major_state = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
else if (ctx->type != 3)
|
||||
CBOR_PARSER_ERROR("Expected null or string, got %u-%u", ctx->type, ctx->value);
|
||||
|
||||
ASSERT_DIE(!ctx->target_buf);
|
||||
ctx->cfg.cf.name = ctx->target_buf = lp_alloc(ctx->lp, ctx->value + 1);
|
||||
ctx->target_len = ctx->value;
|
||||
|
||||
break;
|
||||
|
||||
case 4: /* telnet listener close */
|
||||
if ((ctx->type != 7) || (ctx->value != 22))
|
||||
@ -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");
|
||||
|
@ -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:
|
||||
|
@ -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;
|
||||
|
@ -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,86 +407,20 @@ 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();
|
||||
|
||||
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);
|
||||
ASSERT_DIE(!he.port_name);
|
||||
he.port_name = name ?: "";
|
||||
he.port_sreq = s;
|
||||
|
||||
uint8_t buf[64];
|
||||
linpool *lp = lp_new(s->pool);
|
||||
@ -501,53 +429,86 @@ hexp_get_telnet(sock *s, const char *name)
|
||||
cbor_add_int(cw, 1);
|
||||
cw->cbor[cw->pt++] = 0xf6;
|
||||
|
||||
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);
|
||||
int e = write(he.s->fd, buf, cw->pt);
|
||||
if (e != cw->pt)
|
||||
bug("sendmsg error handling not implemented, got %d (%m)", e);
|
||||
bug("write error handling not implemented, got %d (%m)", e);
|
||||
|
||||
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;
|
||||
|
||||
sock *s = he.port_sreq;
|
||||
he.port_sreq = NULL;
|
||||
|
||||
if (name[0])
|
||||
{
|
||||
/* 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);
|
||||
|
||||
WALK_TLIST_DELSAFE(hexp_telnet_requestor, r, &hrt->p->requestors)
|
||||
{
|
||||
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);
|
||||
sk_send(s, cw->pt);
|
||||
sk_resume_rx(hcs_loop, s);
|
||||
rfree(lp);
|
||||
}
|
||||
|
||||
birdloop_enter(he.loop);
|
||||
|
Loading…
Reference in New Issue
Block a user