version 1.52, 2012/09/12 12:38:16 |
version 1.61.2.1, 2014/08/10 06:52:20 |
|
|
* Client side routines for rump syscall proxy. |
* Client side routines for rump syscall proxy. |
*/ |
*/ |
|
|
#include "rumpuser_port.h" |
#include <rump/rumpuser_port.h> |
|
|
/* |
/* |
* We use kqueue on NetBSD, poll elsewhere. Theoretically we could |
* We use kqueue on NetBSD, poll elsewhere. Theoretically we could |
|
|
* notifications but defer their handling to a stage where we do not |
* notifications but defer their handling to a stage where we do not |
* hold the communication lock. Taking a signal while holding on to |
* hold the communication lock. Taking a signal while holding on to |
* that lock may cause a deadlock. Therefore, block signals throughout |
* that lock may cause a deadlock. Therefore, block signals throughout |
* the RPC when using poll. This unfortunately means that the normal |
* the RPC when using poll. On Linux, we use signalfd in the same role |
* SIGINT way of stopping a process while it is undergoing rump kernel |
* as kqueue on NetBSD to be able to take signals while waiting for a |
* RPC will not work. If anyone know which Linux system call handles |
* response from the server. |
* the above scenario correctly, I'm all ears. |
|
*/ |
*/ |
|
|
#ifdef __NetBSD__ |
#ifdef __NetBSD__ |
#define USE_KQUEUE |
#define USE_KQUEUE |
#endif |
#endif |
|
#if defined(__linux__) && !defined(__ANDROID__) |
|
#define USE_SIGNALFD |
|
#endif |
|
|
#include <sys/cdefs.h> |
|
__RCSID("$NetBSD$"); |
__RCSID("$NetBSD$"); |
|
|
#include <sys/param.h> |
#include <sys/param.h> |
Line 66 __RCSID("$NetBSD$"); |
|
Line 67 __RCSID("$NetBSD$"); |
|
|
|
#include <assert.h> |
#include <assert.h> |
#include <dlfcn.h> |
#include <dlfcn.h> |
#include <err.h> |
|
#include <errno.h> |
#include <errno.h> |
#include <fcntl.h> |
#include <fcntl.h> |
#include <link.h> |
|
#include <poll.h> |
#include <poll.h> |
#include <pthread.h> |
#include <pthread.h> |
#include <signal.h> |
#include <signal.h> |
Line 87 int (*host_socket)(int, int, int); |
|
Line 86 int (*host_socket)(int, int, int); |
|
int (*host_close)(int); |
int (*host_close)(int); |
int (*host_connect)(int, const struct sockaddr *, socklen_t); |
int (*host_connect)(int, const struct sockaddr *, socklen_t); |
int (*host_fcntl)(int, int, ...); |
int (*host_fcntl)(int, int, ...); |
|
#ifdef __ANDROID__ |
|
int (*host_poll)(struct pollfd *, nfds_t, long); |
|
#else |
int (*host_poll)(struct pollfd *, nfds_t, int); |
int (*host_poll)(struct pollfd *, nfds_t, int); |
|
#endif |
ssize_t (*host_read)(int, void *, size_t); |
ssize_t (*host_read)(int, void *, size_t); |
|
#ifdef __ANDROID__ |
|
int (*host_sendmsg)(int, const struct msghdr *, unsigned int); |
|
#else |
ssize_t (*host_sendmsg)(int, const struct msghdr *, int); |
ssize_t (*host_sendmsg)(int, const struct msghdr *, int); |
|
#endif |
int (*host_setsockopt)(int, int, int, const void *, socklen_t); |
int (*host_setsockopt)(int, int, int, const void *, socklen_t); |
int (*host_dup)(int); |
int (*host_dup)(int); |
|
|
Line 99 int (*host_kevent)(int, const struct kev |
|
Line 106 int (*host_kevent)(int, const struct kev |
|
struct kevent *, size_t, const struct timespec *); |
struct kevent *, size_t, const struct timespec *); |
#endif |
#endif |
|
|
|
#ifdef USE_SIGNALFD |
|
#include <sys/signalfd.h> |
|
|
|
int (*host_signalfd)(int, const sigset_t *, int); |
|
#endif |
|
|
int (*host_execve)(const char *, char *const[], char *const[]); |
int (*host_execve)(const char *, char *const[], char *const[]); |
|
|
#include "sp_common.c" |
#include "sp_common.c" |
|
#include "rumpuser_sigtrans.c" |
|
|
static struct spclient clispc = { |
static struct spclient clispc = { |
.spc_fd = -1, |
.spc_fd = -1, |
}; |
}; |
|
|
static int kq = -1; |
static int holyfd = -1; |
static sigset_t fullset; |
static sigset_t fullset; |
|
|
static int doconnect(void); |
static int doconnect(void); |
Line 160 send_with_recon(struct spclient *spc, st |
|
Line 174 send_with_recon(struct spclient *spc, st |
|
|
|
/* check that we aren't over the limit */ |
/* check that we aren't over the limit */ |
if (retrytimo > 0) { |
if (retrytimo > 0) { |
struct timeval tmp; |
time_t tdiff; |
|
|
gettimeofday(&curtime, NULL); |
gettimeofday(&curtime, NULL); |
timersub(&curtime, &starttime, &tmp); |
tdiff = curtime.tv_sec - starttime.tv_sec; |
if (tmp.tv_sec >= retrytimo) { |
if (starttime.tv_usec > curtime.tv_usec) |
|
tdiff--; |
|
if (tdiff >= retrytimo) { |
fprintf(stderr, "rump_sp: reconnect " |
fprintf(stderr, "rump_sp: reconnect " |
"failed, %lld second timeout\n", |
"failed, %lld second timeout\n", |
(long long)retrytimo); |
(long long)retrytimo); |
Line 237 cliwaitresp(struct spclient *spc, struct |
|
Line 253 cliwaitresp(struct spclient *spc, struct |
|
* typically we don't have a frame waiting |
* typically we don't have a frame waiting |
* when we come in here, so call kevent now |
* when we come in here, so call kevent now |
*/ |
*/ |
rv = host_kevent(kq, NULL, 0, |
rv = host_kevent(holyfd, NULL, 0, |
kev, __arraycount(kev), NULL); |
kev, __arraycount(kev), NULL); |
|
|
if (__predict_false(rv == -1)) { |
if (__predict_false(rv == -1)) { |
Line 267 cliwaitresp(struct spclient *spc, struct |
|
Line 283 cliwaitresp(struct spclient *spc, struct |
|
* determine what happens next. |
* determine what happens next. |
*/ |
*/ |
activity: |
activity: |
#else /* USE_KQUEUE */ |
#else /* !USE_KQUEUE */ |
struct pollfd pfd; |
struct pollfd pfd[2]; |
|
|
pfd.fd = clispc.spc_fd; |
|
pfd.events = POLLIN; |
|
|
|
rv = host_poll(&pfd, 1, -1); |
pfd[0].fd = clispc.spc_fd; |
|
pfd[0].events = POLLIN; |
|
pfd[1].fd = holyfd; |
|
pfd[1].events = POLLIN; |
|
|
|
rv = host_poll(pfd, 2, -1); |
|
if (rv >= 1 && pfd[1].revents & POLLIN) { |
|
dosig = 1; |
|
goto cleanup; |
|
} |
#endif /* !USE_KQUEUE */ |
#endif /* !USE_KQUEUE */ |
|
|
switch (readframe(spc)) { |
switch (readframe(spc)) { |
Line 591 handlereq(struct spclient *spc) |
|
Line 613 handlereq(struct spclient *spc) |
|
void *mapaddr; |
void *mapaddr; |
size_t maplen; |
size_t maplen; |
int reqtype = spc->spc_hdr.rsp_type; |
int reqtype = spc->spc_hdr.rsp_type; |
|
int sig; |
|
|
switch (reqtype) { |
switch (reqtype) { |
case RUMPSP_COPYIN: |
case RUMPSP_COPYIN: |
Line 617 handlereq(struct spclient *spc) |
|
Line 640 handlereq(struct spclient *spc) |
|
/*LINTED*/ |
/*LINTED*/ |
maplen = *(size_t *)spc->spc_buf; |
maplen = *(size_t *)spc->spc_buf; |
mapaddr = mmap(NULL, maplen, PROT_READ|PROT_WRITE, |
mapaddr = mmap(NULL, maplen, PROT_READ|PROT_WRITE, |
MAP_ANON, -1, 0); |
MAP_ANON|MAP_PRIVATE, -1, 0); |
if (mapaddr == MAP_FAILED) |
if (mapaddr == MAP_FAILED) |
mapaddr = NULL; |
mapaddr = NULL; |
DPRINTF(("rump_sp handlereq: anonmmap: %p\n", mapaddr)); |
DPRINTF(("rump_sp handlereq: anonmmap: %p\n", mapaddr)); |
send_anonmmap_resp(spc, spc->spc_hdr.rsp_reqno, mapaddr); |
send_anonmmap_resp(spc, spc->spc_hdr.rsp_reqno, mapaddr); |
break; |
break; |
case RUMPSP_RAISE: |
case RUMPSP_RAISE: |
DPRINTF(("rump_sp handlereq: raise sig %d\n", rhdr->rsp_signo)); |
sig = rumpuser__sig_rump2host(rhdr->rsp_signo); |
raise((int)rhdr->rsp_signo); |
DPRINTF(("rump_sp handlereq: raise sig %d\n", sig)); |
|
raise(sig); |
/* |
/* |
* We most likely have signals blocked, but the signal |
* We most likely have signals blocked, but the signal |
* will be handled soon enough when we return. |
* will be handled soon enough when we return. |
Line 675 dupgood(int myfd, int mustchange) |
|
Line 699 dupgood(int myfd, int mustchange) |
|
return myfd; |
return myfd; |
} |
} |
|
|
|
#if defined(USE_KQUEUE) |
|
|
|
static int |
|
makeholyfd(void) |
|
{ |
|
struct kevent kev[NSIG+1]; |
|
int i, fd; |
|
|
|
/* setup kqueue, we want all signals and the fd */ |
|
if ((fd = dupgood(host_kqueue(), 0)) == -1) { |
|
ERRLOG(("rump_sp: cannot setup kqueue")); |
|
return -1; |
|
} |
|
|
|
for (i = 0; i < NSIG; i++) { |
|
EV_SET(&kev[i], i+1, EVFILT_SIGNAL, EV_ADD|EV_ENABLE, 0, 0, 0); |
|
} |
|
EV_SET(&kev[NSIG], clispc.spc_fd, |
|
EVFILT_READ, EV_ADD|EV_ENABLE, 0, 0, 0); |
|
if (host_kevent(fd, kev, NSIG+1, NULL, 0, NULL) == -1) { |
|
ERRLOG(("rump_sp: kevent() failed")); |
|
host_close(fd); |
|
return -1; |
|
} |
|
|
|
return fd; |
|
} |
|
|
|
#elif defined(USE_SIGNALFD) /* !USE_KQUEUE */ |
|
|
|
static int |
|
makeholyfd(void) |
|
{ |
|
|
|
return host_signalfd(-1, &fullset, 0); |
|
} |
|
|
|
#else /* !USE_KQUEUE && !USE_SIGNALFD */ |
|
|
|
static int |
|
makeholyfd(void) |
|
{ |
|
|
|
return -1; |
|
} |
|
|
|
#endif |
|
|
static int |
static int |
doconnect(void) |
doconnect(void) |
{ |
{ |
|
|
int s, error, flags; |
int s, error, flags; |
ssize_t n; |
ssize_t n; |
|
|
if (kq != -1) |
if (holyfd != -1) |
host_close(kq); |
host_close(holyfd); |
kq = -1; |
holyfd = -1; |
s = -1; |
s = -1; |
|
|
if (clispc.spc_fd != -1) |
if (clispc.spc_fd != -1) |
|
|
clispc.spc_fd = s; |
clispc.spc_fd = s; |
clispc.spc_state = SPCSTATE_RUNNING; |
clispc.spc_state = SPCSTATE_RUNNING; |
clispc.spc_reconnecting = 0; |
clispc.spc_reconnecting = 0; |
|
holyfd = makeholyfd(); |
#ifdef USE_KQUEUE |
|
{ |
|
struct kevent kev[NSIG+1]; |
|
int i; |
|
|
|
/* setup kqueue, we want all signals and the fd */ |
|
if ((kq = dupgood(host_kqueue(), 0)) == -1) { |
|
ERRLOG(("rump_sp: cannot setup kqueue")); |
|
return -1; |
|
} |
|
|
|
for (i = 0; i < NSIG; i++) { |
|
EV_SET(&kev[i], i+1, EVFILT_SIGNAL, EV_ADD|EV_ENABLE, 0, 0, 0); |
|
} |
|
EV_SET(&kev[NSIG], clispc.spc_fd, |
|
EVFILT_READ, EV_ADD|EV_ENABLE, 0, 0, 0); |
|
if (host_kevent(kq, kev, NSIG+1, NULL, 0, NULL) == -1) { |
|
ERRLOG(("rump_sp: kevent() failed")); |
|
return -1; |
|
} |
|
} |
|
#endif /* USE_KQUEUE */ |
|
|
|
return 0; |
return 0; |
} |
} |
|
|
return 0; |
return 0; |
} |
} |
|
|
|
#ifdef RTLD_NEXT |
void *rumpclient__dlsym(void *, const char *); |
void *rumpclient__dlsym(void *, const char *); |
void * |
void * |
rumpclient__dlsym(void *handle, const char *symbol) |
rumpclient__dlsym(void *handle, const char *symbol) |
Line 804 rumpclient__dlsym(void *handle, const ch |
|
Line 855 rumpclient__dlsym(void *handle, const ch |
|
} |
} |
void *rumphijack_dlsym(void *, const char *) |
void *rumphijack_dlsym(void *, const char *) |
__attribute__((__weak__, alias("rumpclient__dlsym"))); |
__attribute__((__weak__, alias("rumpclient__dlsym"))); |
|
#endif |
|
|
static pid_t init_done = 0; |
static pid_t init_done = 0; |
|
|
Line 826 rumpclient_init(void) |
|
Line 878 rumpclient_init(void) |
|
if (init_done == (mypid = getpid())) |
if (init_done == (mypid = getpid())) |
return 0; |
return 0; |
|
|
|
#ifdef USE_KQUEUE |
/* kq does not traverse fork() */ |
/* kq does not traverse fork() */ |
if (init_done != 0) |
holyfd = -1; |
kq = -1; |
#endif |
init_done = mypid; |
init_done = mypid; |
|
|
sigfillset(&fullset); |
sigfillset(&fullset); |
Line 837 rumpclient_init(void) |
|
Line 890 rumpclient_init(void) |
|
* sag mir, wo die symbols sind. zogen fort, der krieg beginnt. |
* sag mir, wo die symbols sind. zogen fort, der krieg beginnt. |
* wann wird man je verstehen? wann wird man je verstehen? |
* wann wird man je verstehen? wann wird man je verstehen? |
*/ |
*/ |
|
#ifdef RTLD_NEXT |
#define FINDSYM2(_name_,_syscall_) \ |
#define FINDSYM2(_name_,_syscall_) \ |
if ((host_##_name_ = rumphijack_dlsym(RTLD_NEXT, \ |
if ((host_##_name_ = rumphijack_dlsym(RTLD_NEXT, \ |
#_syscall_)) == NULL) { \ |
#_syscall_)) == NULL) { \ |
if (rumphijack_dlsym == rumpclient__dlsym) \ |
if (rumphijack_dlsym == rumpclient__dlsym) \ |
host_##_name_ = _name_; /* static fallback */ \ |
host_##_name_ = _name_; /* static fallback */ \ |
if (host_##_name_ == NULL) \ |
if (host_##_name_ == NULL) { \ |
errx(1, "cannot find %s: %s", #_syscall_, \ |
fprintf(stderr,"cannot find %s: %s", #_syscall_,\ |
dlerror()); \ |
dlerror()); \ |
|
exit(1); \ |
|
} \ |
} |
} |
|
#else |
|
#define FINDSYM2(_name_,_syscall) \ |
|
host_##_name_ = _name_; |
|
#endif |
#define FINDSYM(_name_) FINDSYM2(_name_,_name_) |
#define FINDSYM(_name_) FINDSYM2(_name_,_name_) |
#ifdef __NetBSD__ |
#ifdef __NetBSD__ |
FINDSYM2(socket,__socket30) |
FINDSYM2(socket,__socket30) |
Line 872 rumpclient_init(void) |
|
Line 932 rumpclient_init(void) |
|
#endif |
#endif |
#endif /* USE_KQUEUE */ |
#endif /* USE_KQUEUE */ |
|
|
|
#ifdef USE_SIGNALFD |
|
FINDSYM(signalfd) |
|
#endif |
|
|
#undef FINDSYM |
#undef FINDSYM |
#undef FINDSY2 |
#undef FINDSY2 |
|
|
Line 892 rumpclient_init(void) |
|
Line 956 rumpclient_init(void) |
|
goto out; |
goto out; |
|
|
if ((p = getenv("RUMPCLIENT__EXECFD")) != NULL) { |
if ((p = getenv("RUMPCLIENT__EXECFD")) != NULL) { |
sscanf(p, "%d,%d", &clispc.spc_fd, &kq); |
sscanf(p, "%d,%d", &clispc.spc_fd, &holyfd); |
unsetenv("RUMPCLIENT__EXECFD"); |
unsetenv("RUMPCLIENT__EXECFD"); |
hstype = HANDSHAKE_EXEC; |
hstype = HANDSHAKE_EXEC; |
} else { |
} else { |
Line 921 rumpclient_init(void) |
|
Line 985 rumpclient_init(void) |
|
struct rumpclient_fork { |
struct rumpclient_fork { |
uint32_t fork_auth[AUTHLEN]; |
uint32_t fork_auth[AUTHLEN]; |
struct spclient fork_spc; |
struct spclient fork_spc; |
int fork_kq; |
int fork_holyfd; |
}; |
}; |
|
|
struct rumpclient_fork * |
struct rumpclient_fork * |
Line 948 rumpclient_prefork(void) |
|
Line 1012 rumpclient_prefork(void) |
|
free(resp); |
free(resp); |
|
|
rpf->fork_spc = clispc; |
rpf->fork_spc = clispc; |
rpf->fork_kq = kq; |
rpf->fork_holyfd = holyfd; |
|
|
out: |
out: |
pthread_sigmask(SIG_SETMASK, &omask, NULL); |
pthread_sigmask(SIG_SETMASK, &omask, NULL); |
Line 965 rumpclient_fork_init(struct rumpclient_f |
|
Line 1029 rumpclient_fork_init(struct rumpclient_f |
|
memset(&clispc, 0, sizeof(clispc)); |
memset(&clispc, 0, sizeof(clispc)); |
clispc.spc_fd = osock; |
clispc.spc_fd = osock; |
|
|
kq = -1; /* kqueue descriptor is not copied over fork() */ |
#ifdef USE_KQUEUE |
|
holyfd = -1; /* kqueue descriptor is not copied over fork() */ |
|
#else |
|
if (holyfd != -1) { |
|
host_close(holyfd); |
|
holyfd = -1; |
|
} |
|
#endif |
|
|
if (doinit() == -1) |
if (doinit() == -1) |
return -1; |
return -1; |
Line 997 rumpclient_fork_vparent(struct rumpclien |
|
Line 1068 rumpclient_fork_vparent(struct rumpclien |
|
{ |
{ |
|
|
clispc = rpf->fork_spc; |
clispc = rpf->fork_spc; |
kq = rpf->fork_kq; |
holyfd = rpf->fork_holyfd; |
} |
} |
|
|
void |
void |
Line 1019 rumpclient__closenotify(int *fdp, enum r |
|
Line 1090 rumpclient__closenotify(int *fdp, enum r |
|
|
|
switch (variant) { |
switch (variant) { |
case RUMPCLIENT_CLOSE_FCLOSEM: |
case RUMPCLIENT_CLOSE_FCLOSEM: |
untilfd = MAX(clispc.spc_fd, kq); |
untilfd = MAX(clispc.spc_fd, holyfd); |
for (; fd <= untilfd; fd++) { |
for (; fd <= untilfd; fd++) { |
if (fd == clispc.spc_fd || fd == kq) |
if (fd == clispc.spc_fd || fd == holyfd) |
continue; |
continue; |
rv = host_close(fd); |
rv = host_close(fd); |
if (rv == -1) |
if (rv == -1) |
Line 1050 rumpclient__closenotify(int *fdp, enum r |
|
Line 1121 rumpclient__closenotify(int *fdp, enum r |
|
EVFILT_READ, EV_DELETE, 0, 0, 0); |
EVFILT_READ, EV_DELETE, 0, 0, 0); |
EV_SET(&kev[1], newfd, |
EV_SET(&kev[1], newfd, |
EVFILT_READ, EV_ADD|EV_ENABLE, 0, 0, 0); |
EVFILT_READ, EV_ADD|EV_ENABLE, 0, 0, 0); |
if (host_kevent(kq, kev, 2, NULL, 0, NULL) == -1) { |
if (host_kevent(holyfd, kev, 2, NULL, 0, NULL) == -1) { |
int sverrno = errno; |
int sverrno = errno; |
host_close(newfd); |
host_close(newfd); |
errno = sverrno; |
errno = sverrno; |
return -1; |
return -1; |
} |
}} |
|
#endif /* !USE_KQUEUE */ |
clispc.spc_fd = newfd; |
clispc.spc_fd = newfd; |
} |
|
} |
} |
if (fd == kq) { |
if (holyfd != -1 && fd == holyfd) { |
newfd = dupgood(kq, 1); |
newfd = dupgood(holyfd, 1); |
if (newfd == -1) |
if (newfd == -1) |
return -1; |
return -1; |
kq = newfd; |
holyfd = newfd; |
#else /* USE_KQUEUE */ |
|
clispc.spc_fd = newfd; |
|
#endif /* !USE_KQUEUE */ |
|
} |
} |
break; |
break; |
} |
} |
Line 1097 rumpclient_exec(const char *path, char * |
|
Line 1165 rumpclient_exec(const char *path, char * |
|
int rv, sverrno; |
int rv, sverrno; |
|
|
snprintf(buf, sizeof(buf), "RUMPCLIENT__EXECFD=%d,%d", |
snprintf(buf, sizeof(buf), "RUMPCLIENT__EXECFD=%d,%d", |
clispc.spc_fd, kq); |
clispc.spc_fd, holyfd); |
envstr = malloc(strlen(buf)+1); |
envstr = malloc(strlen(buf)+1); |
if (envstr == NULL) { |
if (envstr == NULL) { |
return ENOMEM; |
return ENOMEM; |
Line 1144 rumpclient_exec(const char *path, char * |
|
Line 1212 rumpclient_exec(const char *path, char * |
|
return rv; |
return rv; |
} |
} |
|
|
|
/* |
|
* daemon() is handwritten for the benefit of platforms which |
|
* do not support daemon(). |
|
*/ |
int |
int |
rumpclient_daemon(int nochdir, int noclose) |
rumpclient_daemon(int nochdir, int noclose) |
{ |
{ |
Line 1153 rumpclient_daemon(int nochdir, int noclo |
|
Line 1225 rumpclient_daemon(int nochdir, int noclo |
|
if ((rf = rumpclient_prefork()) == NULL) |
if ((rf = rumpclient_prefork()) == NULL) |
return -1; |
return -1; |
|
|
if (daemon(nochdir, noclose) == -1) { |
switch (fork()) { |
sverrno = errno; |
case 0: |
rumpclient_fork_cancel(rf); |
break; |
errno = sverrno; |
case -1: |
return -1; |
goto daemonerr; |
|
default: |
|
_exit(0); |
} |
} |
|
|
|
if (setsid() == -1) |
|
goto daemonerr; |
|
if (!nochdir && chdir("/") == -1) |
|
goto daemonerr; |
|
if (!noclose) { |
|
int fd = open("/dev/null", O_RDWR); |
|
dup2(fd, 0); |
|
dup2(fd, 1); |
|
dup2(fd, 2); |
|
if (fd > 2) |
|
close(fd); |
|
} |
|
|
|
/* note: fork is either completed or cancelled by the call */ |
if (rumpclient_fork_init(rf) == -1) |
if (rumpclient_fork_init(rf) == -1) |
return -1; |
return -1; |
|
|
return 0; |
return 0; |
|
|
|
daemonerr: |
|
sverrno = errno; |
|
rumpclient_fork_cancel(rf); |
|
errno = sverrno; |
|
return -1; |
} |
} |