#include <sys/types.h>
#include <sys/capsicum.h>
#include <sys/procdesc.h>
#include <sys/socket.h>
#include <sys/nv.h>
#include <assert.h>
#include <err.h>
#include <errno.h>
#include <stdbool.h>
#include <stdlib.h>
#include <strings.h>
#include <unistd.h>
#include "libcasper_impl.h"
#include "zygote.h"
static int zygote_sock = -1;
#define ZYGOTE_SERVICE_EXECUTE 1
int
zygote_clone(uint64_t funcidx, int *chanfdp, int *procfdp)
{
nvlist_t *nvl;
int error;
if (zygote_sock == -1) {
errno = ENXIO;
return (-1);
}
nvl = nvlist_create(0);
nvlist_add_number(nvl, "funcidx", funcidx);
nvl = nvlist_xfer(zygote_sock, nvl, 0);
if (nvl == NULL)
return (-1);
if (nvlist_exists_number(nvl, "error")) {
error = (int)nvlist_get_number(nvl, "error");
nvlist_destroy(nvl);
errno = error;
return (-1);
}
*chanfdp = nvlist_take_descriptor(nvl, "chanfd");
*procfdp = nvlist_take_descriptor(nvl, "procfd");
nvlist_destroy(nvl);
return (0);
}
int
zygote_clone_service_execute(int *chanfdp, int *procfdp)
{
return (zygote_clone(ZYGOTE_SERVICE_EXECUTE, chanfdp, procfdp));
}
static void
zygote_main(int *sockp)
{
int error, procfd;
int chanfd[2];
nvlist_t *nvlin, *nvlout;
uint64_t funcidx;
zygote_func_t *func;
pid_t pid;
fd_fix_environment(sockp);
assert(*sockp > STDERR_FILENO);
setproctitle("zygote");
for (;;) {
nvlin = nvlist_recv(*sockp, 0);
if (nvlin == NULL) {
if (errno == ENOTCONN) {
_exit(0);
}
continue;
}
funcidx = nvlist_get_number(nvlin, "funcidx");
nvlist_destroy(nvlin);
switch (funcidx) {
case ZYGOTE_SERVICE_EXECUTE:
func = service_execute;
break;
default:
_exit(0);
}
procfd = -1;
chanfd[0] = -1;
chanfd[1] = -1;
error = 0;
if (socketpair(PF_UNIX, SOCK_STREAM | SOCK_NONBLOCK, 0,
chanfd) == -1) {
error = errno;
goto send;
}
pid = pdfork(&procfd, 0);
switch (pid) {
case -1:
error = errno;
break;
case 0:
close(*sockp);
close(chanfd[0]);
func(chanfd[1]);
_exit(1);
default:
close(chanfd[1]);
break;
}
send:
nvlout = nvlist_create(0);
if (error != 0) {
nvlist_add_number(nvlout, "error", (uint64_t)error);
if (chanfd[0] >= 0)
close(chanfd[0]);
if (procfd >= 0)
close(procfd);
} else {
nvlist_move_descriptor(nvlout, "chanfd", chanfd[0]);
nvlist_move_descriptor(nvlout, "procfd", procfd);
}
(void)nvlist_send(*sockp, nvlout);
nvlist_destroy(nvlout);
}
}
int
zygote_init(void)
{
int serrno, sp[2];
pid_t pid;
if (socketpair(PF_UNIX, SOCK_STREAM | SOCK_NONBLOCK, 0, sp) == -1)
return (-1);
pid = fork();
switch (pid) {
case -1:
serrno = errno;
close(sp[0]);
close(sp[1]);
errno = serrno;
return (-1);
case 0:
close(sp[0]);
zygote_main(&sp[1]);
abort();
default:
zygote_sock = sp[0];
close(sp[1]);
return (0);
}
}