From 9d30aecd299523fa0e6774dc9546466a527cd012 Mon Sep 17 00:00:00 2001 From: Michael Santos Date: Tue, 4 Mar 2014 18:24:07 -0500 Subject: [PATCH] WIP: proxy Convert alcove to a select loop. Each process can call the commands. fork(2) and clone(2) are now just commands. --- bin/alcove_cmd.sh | 4 +- c_src/alcove.c | 317 +++++++++++------------------------------ c_src/alcove.h | 10 +- c_src/alcove_cmd.c | 183 +++++++++++++++++++++--- c_src/alcove_cmd.proto | 2 + src/alcove.app.src | 2 +- 6 files changed, 258 insertions(+), 260 deletions(-) diff --git a/bin/alcove_cmd.sh b/bin/alcove_cmd.sh index e10d0b6..b8cd89e 100755 --- a/bin/alcove_cmd.sh +++ b/bin/alcove_cmd.sh @@ -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; diff --git a/c_src/alcove.c b/c_src/alcove.c index 811dbd0..a5368b5 100644 --- a/c_src/alcove.c +++ b/c_src/alcove.c @@ -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 \n" -#ifdef CLONE_NEWNS - " -n new namespace: ipc, net, ns, pid, user, uts\n" -#endif " -v verbose mode\n", __progname ); diff --git a/c_src/alcove.h b/c_src/alcove.h index 5fb0132..0176451 100644 --- a/c_src/alcove.h +++ b/c_src/alcove.h @@ -24,6 +24,7 @@ #include #include #include +#include #include #include @@ -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) { \ diff --git a/c_src/alcove_cmd.c b/c_src/alcove_cmd.c index c74981d..2888d62 100644 --- a/c_src/alcove_cmd.c +++ b/c_src/alcove_cmd.c @@ -25,13 +25,32 @@ #include +#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; +} diff --git a/c_src/alcove_cmd.proto b/c_src/alcove_cmd.proto index c951146..005c4f5 100644 --- a/c_src/alcove_cmd.proto +++ b/c_src/alcove_cmd.proto @@ -1,6 +1,8 @@ chdir/1 +clone/1 chroot/1 execvp/2 +fork/0 getcwd/0 getgid/0 gethostname/0 diff --git a/src/alcove.app.src b/src/alcove.app.src index 1ca52a7..e8389ac 100644 --- a/src/alcove.app.src +++ b/src/alcove.app.src @@ -1,7 +1,7 @@ {application, alcove, [ {description, "Erlang sandbox for ports"}, - {vsn, "0.1.0"}, + {vsn, "0.2.0"}, {registered, []}, {applications, [ kernel,