WIP: proxy

Convert alcove to a select loop. Each process can call the commands.
fork(2) and clone(2) are now just commands.
This commit is contained in:
Michael Santos
2014-03-04 18:24:07 -05:00
parent 10f9818000
commit 9d30aecd29
6 changed files with 258 additions and 260 deletions

View File

@@ -23,14 +23,14 @@ OIFS=$IFS
while read line; do
IFS=/
set -- $line
printf "static ETERM *alcove_%s(ETERM *);\n" $1
printf "static ETERM *alcove_%s(alcove_state_t *, ETERM *);\n" $1
done < $PROTO
cat<< 'EOF'
/* commands */
typedef struct {
ETERM *(*fp)(ETERM *);
ETERM *(*fp)(alcove_state_t *, ETERM *);
u_int8_t narg;
} alcove_cmd_t;

View File

@@ -32,32 +32,21 @@
#pragma message "Support for namespaces using clone(2) disabled"
#endif
typedef struct {
int ctl[2];
int in[2];
int out[2];
int err[2];
} alcove_fd_t;
static int alcove_call(alcove_state_t *ap, int fd, u_int16_t len);
static alcove_msg_t * alcove_msg(int fd, u_int16_t len);
static int alcove_fork(alcove_state_t *);
static void alcove_ctl(int fd);
static void alcove_select(alcove_state_t *);
static alcove_msg_t *alcove_msg(int);
static void usage(alcove_state_t *);
static int alcove_fork_child(void *arg);
static ssize_t alcove_child_proxy(int, int, u_int16_t);
static int alcove_write(int fd, u_int16_t, ETERM *);
static ssize_t alcove_child_proxy(int fdin, u_int16_t type);
static int alcove_write(u_int16_t, ETERM *);
static ssize_t alcove_read(int, void *, ssize_t);
static void alcove_stats(alcove_state_t *ap);
static void usage(alcove_state_t *);
extern char *__progname;
static int child_exited = 0;
static void
void
gotsig(int sig)
{
switch (sig) {
@@ -81,29 +70,14 @@ main(int argc, char *argv[])
if (!ap)
erl_err_sys("calloc");
ap->pid = -1;
ap->ctl = -1;
ap->fdin = -1;
ap->fdout = -1;
ap->fderr = -1;
signal(SIGCHLD, gotsig);
while ( (ch = getopt(argc, argv, "n:hv")) != -1) {
while ( (ch = getopt(argc, argv, "hv")) != -1) {
switch (ch) {
case 'n':
#ifdef CLONE_NEWNS
if (!strncmp("ipc", optarg, 3)) ap->ns |= CLONE_NEWIPC;
else if (!strncmp("net", optarg, 3)) ap->ns |= CLONE_NEWNET;
else if (!strncmp("ns", optarg, 2)) ap->ns |= CLONE_NEWNS;
else if (!strncmp("pid", optarg, 3)) ap->ns |= CLONE_NEWPID;
else if (!strncmp("uts", optarg, 3)) ap->ns |= CLONE_NEWUTS;
else if (!strncmp("user", optarg, 4)) ap->ns |= CLONE_NEWUSER;
else usage(ap);
#else
usage(ap);
#endif
break;
case 'v':
ap->verbose++;
break;
@@ -113,121 +87,12 @@ main(int argc, char *argv[])
}
}
if (alcove_fork(ap) < 0)
exit (EXIT_FAILURE);
alcove_select(ap);
alcove_ctl(ap);
exit(0);
}
static int
alcove_fork(alcove_state_t *ap)
{
alcove_fd_t fd = {{0}};
#ifdef CLONE_NEWNS
const size_t stack_size = 65536;
char *child_stack = NULL;
child_stack = calloc(stack_size, 1);
if (!child_stack)
erl_err_sys("calloc");
#endif
if (socketpair(AF_UNIX, SOCK_STREAM, 0, fd.ctl) < 0)
erl_err_sys("socketpair");
if ( (pipe(fd.in) < 0)
|| (pipe(fd.out) < 0)
|| (pipe(fd.err) < 0))
erl_err_sys("pipe");
#ifdef CLONE_NEWNS
ap->pid = clone(alcove_fork_child, child_stack + stack_size, ap->ns | SIGCHLD, &fd);
if (ap->pid < 0)
erl_err_sys("clone");
#else
ap->pid = fork();
switch (ap->pid) {
case -1:
erl_err_sys("fork");
case 0:
return alcove_fork_child(ap);
break;
default:
break;
}
#endif
if ( (close(fd.ctl[PIPE_READ]) < 0)
|| (close(fd.in[PIPE_READ]) < 0)
|| (close(fd.out[PIPE_WRITE]) < 0)
|| (close(fd.err[PIPE_WRITE]) < 0))
erl_err_sys("close");
ap->ctl = fd.ctl[PIPE_WRITE];
ap->fdin = fd.in[PIPE_WRITE];
ap->fdout = fd.out[PIPE_READ];
ap->fderr = fd.err[PIPE_READ];
return 0;
}
static int
alcove_fork_child(void *arg)
{
alcove_fd_t *fd = arg;
if ( (close(fd->ctl[PIPE_WRITE]) < 0)
|| (close(fd->in[PIPE_WRITE]) < 0)
|| (close(fd->out[PIPE_READ]) < 0)
|| (close(fd->err[PIPE_READ]) < 0))
erl_err_sys("close");
if ( (dup2(fd->in[PIPE_READ], STDIN_FILENO) < 0)
|| (dup2(fd->out[PIPE_WRITE], STDOUT_FILENO) < 0)
|| (dup2(fd->err[PIPE_WRITE], STDERR_FILENO) < 0))
erl_err_sys("dup2");
alcove_ctl(fd->ctl[PIPE_READ]);
return 0;
}
static void
alcove_ctl(int fd)
{
alcove_msg_t *msg = NULL;
ETERM *arg = NULL;
ETERM *reply = NULL;
for ( ; ; ) {
msg = alcove_msg(fd);
if (!msg)
break;
arg = erl_decode(msg->arg);
if (!arg)
erl_err_quit("invalid message");
reply = alcove_cmd(msg->cmd, arg);
if (!reply)
erl_err_quit("unrecoverable error");
free(msg->arg);
free(msg);
erl_free_compound(arg);
if (alcove_write(fd, ALCOVE_MSG_CALL, reply) < 0)
erl_err_sys("alcove_write");
erl_free_compound(reply);
}
}
static void
alcove_select(alcove_state_t *ap)
void
alcove_ctl(alcove_state_t *ap)
{
fd_set rfds;
int fdmax = 0;
@@ -238,9 +103,7 @@ alcove_select(alcove_state_t *ap)
return;
FD_ZERO(&rfds);
FD_SET(STDIN_FILENO, &rfds);
fdmax = STDIN_FILENO;
if (ap->fdout > -1) {
@@ -253,11 +116,6 @@ alcove_select(alcove_state_t *ap)
fdmax = MAX(fdmax, ap->fderr);
}
if (ap->ctl > -1) {
FD_SET(ap->ctl, &rfds);
fdmax = MAX(fdmax, ap->ctl);
}
rv = select(fdmax+1, &rfds, NULL, NULL, NULL);
if (rv < 0) {
@@ -270,24 +128,6 @@ alcove_select(alcove_state_t *ap)
}
}
if (FD_ISSET(ap->ctl, &rfds)) {
ssize_t n = 0;
char buf[65535] = {0};
n = read(ap->ctl, buf, sizeof(buf));
if (n == 0) {
ap->ctl = -1;
}
else if (n < 0) {
erl_err_quit("eof");
}
/* XXX lock stdout */
if (write(STDOUT_FILENO, buf, n) != n)
erl_err_sys("write:ctl->stdout");
}
if (FD_ISSET(STDIN_FILENO, &rfds)) {
u_int16_t bufsz = 0;
u_int16_t type = 0;
@@ -303,23 +143,21 @@ alcove_select(alcove_state_t *ap)
erl_err_sys("read:stdin");
}
bufsz = ntohs(bufsz);
bufsz -= sizeof(type);
/* message type */
if (alcove_read(STDIN_FILENO, &type, sizeof(type)) != sizeof(type))
erl_err_sys("read:stdin");
bufsz = ntohs(bufsz);
bufsz -= sizeof(type);
if (alcove_read(STDIN_FILENO, buf, bufsz) != bufsz)
erl_err_sys("read:stdin");
if (type == ALCOVE_MSG_CALL) {
u_int16_t size = htons(bufsz);
if ( (write(ap->ctl, &size, 2) != 2)
|| (write(ap->ctl, buf, bufsz) != bufsz))
erl_err_sys("write:stdin->ctl");
if (alcove_call(ap, STDIN_FILENO, bufsz) < 0)
erl_err_quit("call");
}
else if (type == ALCOVE_MSG_CHILDIN) {
if (alcove_read(STDIN_FILENO, buf, bufsz) != bufsz)
erl_err_sys("read:stdin");
if (write(ap->fdin, buf, bufsz) != bufsz)
erl_err_sys("write:stdin->childin");
}
@@ -330,7 +168,7 @@ alcove_select(alcove_state_t *ap)
}
if (FD_ISSET(ap->fdout, &rfds)) {
switch (alcove_child_proxy(ap->fdout, STDOUT_FILENO, ALCOVE_MSG_CHILDOUT)) {
switch (alcove_child_proxy(ap->fdout, ALCOVE_MSG_CHILDOUT)) {
case -1:
erl_err_sys("alcove_child_proxy");
case 0:
@@ -341,7 +179,7 @@ alcove_select(alcove_state_t *ap)
}
if (FD_ISSET(ap->fderr, &rfds)) {
switch (alcove_child_proxy(ap->fderr, STDOUT_FILENO, ALCOVE_MSG_CHILDERR)) {
switch (alcove_child_proxy(ap->fderr, ALCOVE_MSG_CHILDERR)) {
case -1:
erl_err_sys("alcove_child_proxy");
case 0:
@@ -353,56 +191,76 @@ alcove_select(alcove_state_t *ap)
}
}
static alcove_msg_t *
alcove_msg(int fd)
static int
alcove_call(alcove_state_t *ap, int fd, u_int16_t len)
{
ssize_t n = 0;
u_int16_t buf = 0;
u_int16_t len = 0;
alcove_msg_t *msg = NULL;
ETERM *arg = NULL;
ETERM *reply = NULL;
int rv = -1;
msg = alcove_msg(fd, len);
if (!msg)
return -1;
arg = erl_decode(msg->arg);
if (!arg)
goto DONE;
reply = alcove_cmd(ap, msg->cmd, arg);
if (!reply)
goto DONE;
rv = alcove_write(ALCOVE_MSG_CALL, reply);
DONE:
free(msg->arg);
free(msg);
erl_free_compound(arg);
erl_free_compound(reply);
return rv;
}
static alcove_msg_t *
alcove_msg(int fd, u_int16_t len)
{
u_int16_t cmd = 0;
alcove_msg_t *msg = NULL;
errno = 0;
n = alcove_read(fd, &buf, sizeof(buf));
if (len <= sizeof(cmd))
return NULL;
if (n != sizeof(buf)) {
if (errno == 0)
return NULL;
erl_err_sys("alcove_msg: expected=%lu, got=%lu",
(unsigned long)sizeof(buf),
(unsigned long)n);
}
len = ntohs(buf);
if (len >= UINT16_MAX || len < sizeof(buf))
erl_err_quit("alcove_msg: invalid len=%d (max=%d)", len, UINT16_MAX);
len -= sizeof(buf);
msg = alcove_malloc(sizeof(alcove_msg_t));
msg->arg = alcove_malloc(len);
len -= sizeof(cmd);
/* cmd */
n = alcove_read(fd, &buf, sizeof(buf));
if (n != sizeof(buf))
erl_err_sys("alcove_msg: expected=%lu, got=%lu",
(unsigned long)sizeof(buf),
(unsigned long)n);
if (alcove_read(fd, &cmd, sizeof(cmd)) != sizeof(cmd))
return NULL;
msg->cmd = ntohs(buf);
msg = alcove_malloc(sizeof(alcove_msg_t));
msg->cmd = ntohs(cmd);
/* arg */
n = alcove_read(fd, msg->arg, len);
if (n != len)
erl_err_sys("alcove_msg: expected=%u, got=%lu",
len, (unsigned long)n);
msg->arg = alcove_malloc(len);
if (alcove_read(fd, msg->arg, len) != len)
goto BADARG;
return msg;
BADARG:
if (msg) {
if (msg->arg)
free(msg->arg);
free(msg);
}
return NULL;
}
static ssize_t
alcove_child_proxy(int fdin, int fdout, u_int16_t type)
alcove_child_proxy(int fdin, u_int16_t type)
{
ssize_t n = 0;
char buf[65535] = {0};
@@ -417,20 +275,14 @@ alcove_child_proxy(int fdin, int fdout, u_int16_t type)
return -1;
}
if (alcove_write(fdout, type, erl_mk_binary(buf,n)) < 0)
if (alcove_write(type, erl_mk_binary(buf,n)) < 0)
erl_err_sys("alcove_write");
return n;
}
int
alcove_send(ETERM *t)
{
return alcove_write(STDOUT_FILENO, ALCOVE_MSG_CAST, t);
}
static int
alcove_write(int fd, u_int16_t type, ETERM *t)
alcove_write(u_int16_t type, ETERM *t)
{
int tlen = 0;
u_int16_t hlen = 0;
@@ -447,14 +299,14 @@ alcove_write(int fd, u_int16_t type, ETERM *t)
if (erl_encode(t, buf) < 1)
goto ERR;
// flockfile(stdout);
flockfile(stdout);
if ( (write(fd, &hlen, 2) != 2) ||
(write(fd, &type, 2) != 2) ||
(write(fd, buf, tlen) != tlen))
if ( (write(STDOUT_FILENO, &hlen, 2) != 2) ||
(write(STDOUT_FILENO, &type, 2) != 2) ||
(write(STDOUT_FILENO, buf, tlen) != tlen))
goto ERR;
// funlockfile(stdout);
funlockfile(stdout);
erl_free(buf);
return 0;
@@ -497,9 +349,6 @@ usage(alcove_state_t *ap)
__progname, ALCOVE_VERSION);
(void)fprintf(stderr,
"usage: %s <options>\n"
#ifdef CLONE_NEWNS
" -n <namespace> new namespace: ipc, net, ns, pid, user, uts\n"
#endif
" -v verbose mode\n",
__progname
);

View File

@@ -24,6 +24,7 @@
#include <sys/resource.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <signal.h>
#include <erl_driver.h>
#include <erl_interface.h>
@@ -39,12 +40,9 @@ enum {
typedef struct {
u_int32_t opt;
u_int8_t verbose;
pid_t pid;
int ctl;
int fdin;
int fdout;
int fderr;
int ns;
} alcove_state_t;
typedef struct {
@@ -52,7 +50,9 @@ typedef struct {
unsigned char *arg;
} alcove_msg_t;
int alcove_send(ETERM *t);
void gotsig(int sig);
void alcove_ctl(alcove_state_t *ap);
void *alcove_malloc(ssize_t size);
@@ -65,7 +65,7 @@ ETERM *alcove_ok(ETERM *);
ETERM *alcove_bool(bool);
ETERM *alcove_bin(const char *);
ETERM *alcove_cmd(u_int32_t, ETERM *);
ETERM *alcove_cmd(alcove_state_t *, u_int32_t, ETERM *);
#define VERBOSE(x, ...) do { \
if (ap->verbose >= x) { \

View File

@@ -25,13 +25,32 @@
#include <sys/types.h>
#define PIPE_READ 0
#define PIPE_WRITE 1
typedef struct {
int ctl[2];
int in[2];
int out[2];
int err[2];
} alcove_fd_t;
typedef struct {
alcove_state_t *ap;
alcove_fd_t *fd;
} alcove_child_t;
static char **alcove_list_to_argv(ETERM *);
static void alcove_free_argv(char **);
static int alcove_stdio(alcove_fd_t *fd);
static int alcove_child_fun(void *arg);
static int alcove_parent_fd(alcove_state_t *ap, alcove_fd_t *fd);
#define ALCOVE_IS_IOLIST(_t) (ERL_IS_BINARY(_t) || ERL_IS_LIST(_t))
ETERM *
alcove_cmd(u_int32_t cmd, ETERM *arg)
alcove_cmd(alcove_state_t *ap, u_int32_t cmd, ETERM *arg)
{
alcove_cmd_t *fun = NULL;
@@ -43,11 +62,11 @@ alcove_cmd(u_int32_t cmd, ETERM *arg)
if (!ERL_IS_LIST(arg) || erl_length(arg) != fun->narg)
return erl_mk_atom("badarg");
return (*fun->fp)(arg);
return (*fun->fp)(ap, arg);
}
static ETERM *
alcove_version(ETERM *arg)
alcove_version(alcove_state_t *ap, ETERM *arg)
{
return alcove_bin(ALCOVE_VERSION);
}
@@ -57,7 +76,7 @@ alcove_version(ETERM *arg)
*
*/
static ETERM *
alcove_chdir(ETERM *arg)
alcove_chdir(alcove_state_t *ap, ETERM *arg)
{
ETERM *hd = NULL;
char *path = NULL;
@@ -90,7 +109,7 @@ BADARG:
*
*/
static ETERM *
alcove_chroot(ETERM *arg)
alcove_chroot(alcove_state_t *ap, ETERM *arg)
{
ETERM *hd = NULL;
char *path = NULL;
@@ -118,12 +137,58 @@ BADARG:
return erl_mk_atom("badarg");
}
/*
* clone(2)
*
*/
static ETERM *
alcove_clone(alcove_state_t *ap, ETERM *arg)
{
ETERM *hd = NULL;
alcove_child_t child_arg = {0};
alcove_fd_t fd = {{0}};
const size_t stack_size = 65536;
char *child_stack = NULL;
int flags = 0;
pid_t pid = 0;
/* flags */
arg = alcove_list_head(&hd, arg);
if (!hd || !ERL_IS_INTEGER(hd))
goto BADARG;
flags = ERL_INT_UVALUE(hd);
child_stack = calloc(stack_size, 1);
if (!child_stack)
return alcove_errno(errno);
if (alcove_stdio(&fd) < 0)
return alcove_errno(errno);
child_arg.ap = ap;
child_arg.fd = &fd;
pid = clone(alcove_child_fun, child_stack + stack_size, flags | SIGCHLD, &child_arg);
if (pid < 0)
return alcove_errno(errno);
if (alcove_parent_fd(ap, &fd) < 0)
return alcove_errno(errno);
return alcove_ok(erl_mk_int(pid));
BADARG:
return erl_mk_atom("badarg");
}
/*
* execvp(3)
*
*/
static ETERM *
alcove_execvp(ETERM *arg)
alcove_execvp(alcove_state_t *ap, ETERM *arg)
{
ETERM *hd = NULL;
char *progname = NULL;
@@ -167,12 +232,42 @@ BADARG:
return erl_mk_atom("badarg");
}
/*
* fork(2)
*
*/
static ETERM *
alcove_fork(alcove_state_t *ap, ETERM *arg)
{
alcove_fd_t fd = {{0}};
pid_t pid = 0;
if (alcove_stdio(&fd) < 0)
return alcove_errno(errno);
pid = fork();
switch (pid) {
case -1:
return alcove_errno(errno);
case 0:
alcove_ctl(ap);
erl_err_sys("fork");
default:
if (alcove_parent_fd(ap, &fd) < 0)
return alcove_errno(errno);
return alcove_ok(erl_mk_int(pid));
}
}
/*
* getcwd(3)
*
*/
static ETERM *
alcove_getcwd(ETERM *arg)
alcove_getcwd(alcove_state_t *ap, ETERM *arg)
{
char buf[PATH_MAX] = {0};
@@ -187,7 +282,7 @@ alcove_getcwd(ETERM *arg)
*
*/
static ETERM *
alcove_getgid(ETERM *arg)
alcove_getgid(alcove_state_t *ap, ETERM *arg)
{
return erl_mk_uint(getgid());
}
@@ -197,7 +292,7 @@ alcove_getgid(ETERM *arg)
*
*/
static ETERM *
alcove_getpid(ETERM *arg)
alcove_getpid(alcove_state_t *ap, ETERM *arg)
{
return erl_mk_uint(getpid());
}
@@ -207,7 +302,7 @@ alcove_getpid(ETERM *arg)
*
*/
static ETERM *
alcove_getuid(ETERM *arg)
alcove_getuid(alcove_state_t *ap, ETERM *arg)
{
return erl_mk_uint(getuid());
}
@@ -217,7 +312,7 @@ alcove_getuid(ETERM *arg)
*
*/
static ETERM *
alcove_setgid(ETERM *arg)
alcove_setgid(alcove_state_t *ap, ETERM *arg)
{
ETERM *hd = NULL;
gid_t gid = {0};
@@ -243,7 +338,7 @@ BADARG:
*
*/
static ETERM *
alcove_setuid(ETERM *arg)
alcove_setuid(alcove_state_t *ap, ETERM *arg)
{
ETERM *hd = NULL;
uid_t uid = {0};
@@ -269,7 +364,7 @@ BADARG:
*
*/
static ETERM *
alcove_gethostname(ETERM *arg)
alcove_gethostname(alcove_state_t *ap, ETERM *arg)
{
char name[HOST_NAME_MAX] = {0};
int rv = 0;
@@ -286,7 +381,7 @@ alcove_gethostname(ETERM *arg)
*
*/
static ETERM *
alcove_sethostname(ETERM *arg)
alcove_sethostname(alcove_state_t *ap, ETERM *arg)
{
ETERM *hd = NULL;
char *name = NULL;
@@ -319,7 +414,7 @@ BADARG:
*
*/
static ETERM *
alcove_getrlimit(ETERM *arg)
alcove_getrlimit(alcove_state_t *ap, ETERM *arg)
{
ETERM *hd = NULL;
int resource = 0;
@@ -353,7 +448,7 @@ BADARG:
*
*/
static ETERM *
alcove_setrlimit(ETERM *arg)
alcove_setrlimit(alcove_state_t *ap, ETERM *arg)
{
ETERM *hd = NULL;
int resource = 0;
@@ -400,7 +495,7 @@ BADARG:
*
*/
static ETERM *
alcove_setns(ETERM *arg)
alcove_setns(alcove_state_t *ap, ETERM *arg)
{
ETERM *hd = NULL;
char *path = NULL;
@@ -437,7 +532,7 @@ BADARG:
*
*/
static ETERM *
alcove_unshare(ETERM *arg)
alcove_unshare(alcove_state_t *ap, ETERM *arg)
{
ETERM *hd = NULL;
int flags = 0;
@@ -512,3 +607,55 @@ alcove_free_argv(char **argv)
free(argv);
}
static int
alcove_stdio(alcove_fd_t *fd)
{
if ( (pipe(fd->in) < 0)
|| (pipe(fd->out) < 0)
|| (pipe(fd->err) < 0))
return -1;
return 0;
}
static int
alcove_child_fun(void *arg)
{
alcove_child_t *child_arg = arg;
alcove_state_t *ap = child_arg->ap;
alcove_fd_t *fd = child_arg->fd;
if ( (close(fd->in[PIPE_WRITE]) < 0)
|| (close(fd->out[PIPE_READ]) < 0)
|| (close(fd->err[PIPE_READ]) < 0))
return -1;
if ( (dup2(fd->in[PIPE_READ], STDIN_FILENO) < 0)
|| (dup2(fd->out[PIPE_WRITE], STDOUT_FILENO) < 0)
|| (dup2(fd->err[PIPE_WRITE], STDERR_FILENO) < 0))
return -1;
ap->fdin = fd->in[PIPE_READ];
ap->fdout = fd->out[PIPE_WRITE];
ap->fderr = fd->err[PIPE_WRITE];
alcove_ctl(ap);
return 0;
}
static int
alcove_parent_fd(alcove_state_t *ap, alcove_fd_t *fd)
{
if ( (close(fd->in[PIPE_READ]) < 0)
|| (close(fd->out[PIPE_WRITE]) < 0)
|| (close(fd->err[PIPE_WRITE]) < 0))
return -1;
ap->fdin = fd->in[PIPE_WRITE];
ap->fdout = fd->out[PIPE_READ];
ap->fderr = fd->err[PIPE_READ];
return 0;
}

View File

@@ -1,6 +1,8 @@
chdir/1
clone/1
chroot/1
execvp/2
fork/0
getcwd/0
getgid/0
gethostname/0

View File

@@ -1,7 +1,7 @@
{application, alcove,
[
{description, "Erlang sandbox for ports"},
{vsn, "0.1.0"},
{vsn, "0.2.0"},
{registered, []},
{applications, [
kernel,