diff --git a/distrib/sets/lists/minix-base/mi b/distrib/sets/lists/minix-base/mi index d2a9d8b1d..fde7a4bec 100644 --- a/distrib/sets/lists/minix-base/mi +++ b/distrib/sets/lists/minix-base/mi @@ -872,6 +872,9 @@ ./usr/lib/librump.so.0.0 minix-base rump ./usr/lib/librump.so.0 minix-base rump ./usr/lib/librump.so minix-base rump +./usr/lib/librumpclient.so.0.0 minix-base rump +./usr/lib/librumpclient.so.0 minix-base rump +./usr/lib/librumpclient.so minix-base rump ./usr/lib/librumpdev.so.0.0 minix-base rump ./usr/lib/librumpdev.so.0 minix-base rump ./usr/lib/librumpdev.so minix-base rump diff --git a/distrib/sets/lists/minix-comp/mi b/distrib/sets/lists/minix-comp/mi index 3ba2de230..e5510b432 100644 --- a/distrib/sets/lists/minix-comp/mi +++ b/distrib/sets/lists/minix-comp/mi @@ -1724,6 +1724,7 @@ ./usr/include/rump/rump_namei.h minix-comp rump ./usr/include/rump/rump_syscalls.h minix-comp rump ./usr/include/rump/rump_syscalls_compat.h minix-comp rump +./usr/include/rump/rumpclient.h minix-comp rump ./usr/include/rump/rumpdefs.h minix-comp rump ./usr/include/rump/rumperr.h minix-comp rump ./usr/include/rump/rumperrno2host.h minix-comp rump @@ -2304,6 +2305,8 @@ ./usr/lib/librmt.a minix-comp ./usr/lib/librump.a minix-comp rump ./usr/lib/librump_pic.a minix-comp rump +./usr/lib/librumpclient.a minix-comp rump +./usr/lib/librumpclient_pic.a minix-comp rump ./usr/lib/librumpdev.a minix-comp rump ./usr/lib/librumpdev_audio.a minix-comp rump ./usr/lib/librumpdev_audio_pic.a minix-comp rump diff --git a/distrib/sets/lists/minix-man/mi b/distrib/sets/lists/minix-man/mi index e9ee453e6..47ada63ed 100644 --- a/distrib/sets/lists/minix-man/mi +++ b/distrib/sets/lists/minix-man/mi @@ -2716,6 +2716,7 @@ ./usr/man/man3/rump.3 minix-man rump ./usr/man/man3/rump_etfs.3 minix-man rump ./usr/man/man3/rump_lwproc.3 minix-man rump +./usr/man/man3/rumpclient.3 minix-man rump ./usr/man/man3/ruserok.3 minix-man ./usr/man/man3/saslc.d.3 minix-man crypto ./usr/man/man3/saslc_alloc.3 minix-man crypto diff --git a/lib/Makefile b/lib/Makefile index a7badf1ad..42d4cdf71 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -99,7 +99,7 @@ SUBDIR+= libc_vfp .endif .endif .if (${MKRUMP} != "no") -#SUBDIR+= librumpclient +SUBDIR+= librumpclient .endif .if (${MKSKEY} != "no") SUBDIR+= libskey diff --git a/lib/librumpclient/Makefile b/lib/librumpclient/Makefile new file mode 100644 index 000000000..d6c474663 --- /dev/null +++ b/lib/librumpclient/Makefile @@ -0,0 +1,40 @@ +# $NetBSD: Makefile,v 1.10 2013/09/10 17:39:10 pooka Exp $ +# + +.PATH: ${.CURDIR}/../../sys/rump/librump/rumpkern + +LIB= rumpclient +USE_SHLIBDIR= yes +MAN= rumpclient.3 + +.if defined(__MINIX) +RUMPUSER_THREADS=fiber +.endif + +.include + +INCS= rumpclient.h +INCSDIR= /usr/include/rump + +.for lib in ${RUMPCLIENT_EXTERNAL_DPLIBS} +LIBDO.${lib}= _external +LIBDPLIBS+= ${lib} lib +.endfor + +CPPFLAGS+= -DRUMP_CLIENT +CPPFLAGS+= -I${.OBJDIR} -I${.CURDIR} +CPPFLAGS+= -I${.CURDIR}/../librumpuser +SRCS= rumpclient.c +SRCS+= rump_syscalls.c + +CLEANFILES+= srcsys + +COPTS.rump_syscalls.c+= -fno-strict-aliasing + +.if !make(obj) && !make(clean) && !make(cleandir) +.BEGIN: + @rm -f srcsys + @ln -s ${NETBSDSRCDIR}/sys/sys srcsys +.endif + +.include diff --git a/lib/librumpclient/rumpclient.3 b/lib/librumpclient/rumpclient.3 new file mode 100644 index 000000000..48f0d14b7 --- /dev/null +++ b/lib/librumpclient/rumpclient.3 @@ -0,0 +1,204 @@ +.\" $NetBSD: rumpclient.3,v 1.3 2013/03/08 08:30:44 wiz Exp $ +.\" +.\" Copyright (c) 2011 Antti Kantee. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.Dd February 16, 2011 +.Dt RUMPCLIENT 3 +.Os +.Sh NAME +.Nm rumpclient +.Nd rump client library +.Sh LIBRARY +.Lb rumpclient +.Sh SYNOPSIS +.In rump/rumpclient.h +.In rump/rump_syscalls.h +.Ft int +.Fn rumpclient_init +.Ft pid_t +.Fn rumpclient_fork +.Ft pid_t +.Fn rumpclient_vfork +.Ft struct rumpclient_fork * +.Fn rumpclient_prefork +.Ft int +.Fn rumpclient_fork_init "struct rumpclient_fork *rfp" +.Ft void +.Fn rumpclient_fork_cancel "struct rumpclient_fork *rfp" +.Ft int +.Fn rumpclient_exec "const char *path" "char *const argv[]" "char *const envp[]" +.Ft int +.Fn rumpclient_daemon "int nochdir" "int noclose" +.Ft void +.Fn rumpclient_setconnretry "time_t retrytime" +.Ft int +.Fo rumpclient_syscall +.Fa "int num" "const void *sysarg" "size_t argsize" "register_t *retval" +.Fc +.Sh DESCRIPTION +.Nm +is the clientside implementation of the +.Xr rump_sp 7 +facility. +It can be used to connect to a rump kernel server and make system call +style requests. +.Pp +Every connection to a rump kernel server creates a new process +context in the rump kernel. +By default a process is inherited from init, but through existing +connections and the forking facility offered by +.Nm +it is possible to form process trees. +.Bl -tag -width xxxx +.It Fn rumpclient_init +Initialize +.Nm . +The server address is determined from the environment variable +.Ev RUMP_SERVER +according to syntax described in +.Xr rump_sp 7 . +The new process is registered to the rump kernel with the command +name from +.Xr getprogname 3 . +.It Fn rumpclient_fork +Fork a rump client process. +This also causes a host process fork via +.Xr fork 2 . +The child will have a copy of the parent's rump kernel file descriptors. +.It Fn rumpclient_vfork +Like above, but the host uses +.Xr vfork 2 . +.It Fn rumpclient_prefork +Low-level routine which instructs the rump kernel that the current +process is planning to fork. +The routine returns a +.Pf non- Dv NULL +cookie if successful. +.It Fn rumpclient_fork_init rfp +Low-level routine which works like +.Fn rumpclient_init , +with the exception that it uses the +.Ar rfp +context created by a call to +.Fn rumpclient_prefork . +This is typically called from the child of a +.Xr fork 2 +call. +.It Fn rumpclient_fork_cancel rfp +Cancel previously initiated prefork context. +This is useful for error handling in case a full fork could not +be carried through. +.It Fn rumpclient_exec path argv envp +This call is a +.Nm +wrapper around +.Xr execve 2 . +The wrapper makes sure that the rump kernel process context stays +the same in the newly executed program. +This means that the rump kernel PID remains the same and the same +rump file descriptors are available (apart from ones which +were marked with +.Dv FD_CLOEXEC ) . +.Pp +It should be noted that the newly executed program must call +.Fn rumpclient_init +before any other rump kernel communication can take place. +The wrapper cannot do it because it no longer has program control. +However, since all rump clients call the init routine, +this should not be a problem. +.It Fn rumpclient_daemon noclose nochdir +This function performs the equivalent of +.Xr daemon 3 , +but also ensures that the internal call to +.Xr fork 2 +is handled properly. +This routine is provided for convenience. +.It Fn rumpclient_setconnretry retrytime +Set the timeout for how long the client attempts to reconnect to +the server in case of a broken connection. +After the timeout expires the client will return a failure +for that particular request. +It is critical to note that after a restablished connection the +rump kernel context will be that of a newly connected client. +This means all previous kernel state such as file descriptors +will be lost. +It is largely up to a particular application if this has impact +or not. +For example, web browsers tend to recover fairly smoothly from a +kernel server reconnect, while +.Xr sshd 8 +gets confused if its sockets go missing. +.Pp +If +.Ar retrytime +is a positive integer, it means the number of seconds for which +reconnection will be attempted. +The value 0 means that reconnection will not be attempted, and all +subsequent operations will return the errno +.Er ENOTCONN . +.Pp +Additionally, the following special values are accepted: +.Bl -tag -width xxxx +.It Dv RUMPCLIENT_RETRYCONN_INFTIME +Attempt reconnection indefinitely. +.It Dv RUMPCLIENT_RETRYCONN_ONCE +Attempt reconnect exactly once. +What this precisely means depends on the situation: e.g. getting +.Er EHOSTUNREACH +immediately or the TCP connection request timeouting are considered +to be one retry. +.It Dv RUMPCLIENT_RETRYCONN_DIE +In case of a broken connection is detected at runtime, call +.Xr exit 3 . +This is useful for example in testing. +It ensures that clients are killed immediately when they attempt +to communicate with a halted server. +.El +.It Fn rumpclient_syscall num sysarg argsize retval +Execute an "indirect" system call. +In the normal case system calls are executed through the interfaces in +.In rump/rump_syscalls.h +(for example +.Fn rump_sys_read fd buf nbytes ) . +This interface allows calling the server with pre-marshalled arguments. +.El +.Pp +Additionally, all of the supported rump system calls are available +through this library. +See +.In rump/rump_syscalls.h +for a list. +.Sh RETURN VALUES +.Nm +routines return \-1 in case of error and set errno. +In case of success a non-negative integer is returned, where applicable. +.Sh SEE ALSO +.Xr rump_server 1 , +.Xr rump 3 , +.Xr rump_sp 7 +.Sh CAVEATS +Interfaces for a cryptographically authenticated client-server +handshake do not currently exist. +This can be worked around with e.g. host access control and an ssh +tunnel. diff --git a/lib/librumpclient/rumpclient.c b/lib/librumpclient/rumpclient.c new file mode 100644 index 000000000..4a097f204 --- /dev/null +++ b/lib/librumpclient/rumpclient.c @@ -0,0 +1,1267 @@ +/* $NetBSD: rumpclient.c,v 1.65 2015/01/17 19:34:50 justin Exp $ */ + +/* + * Copyright (c) 2010, 2011 Antti Kantee. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Client side routines for rump syscall proxy. + */ + +#include + +/* + * We use kqueue on the BSDs, poll elsewhere. We + * want to use kqueue because it will give us the ability to get signal + * notifications but defer their handling to a stage where we do not + * hold the communication lock. Taking a signal while holding on to + * that lock may cause a deadlock. Therefore, block signals throughout + * the RPC when using poll. On Linux, we use signalfd in the same role + * as kqueue on NetBSD to be able to take signals while waiting for a + * response from the server. + */ + +#if defined(__NetBSD__) || defined(__FreeBSD__) || \ + defined(__DragonFly__) || defined(__OpenBSD__) +#define USE_KQUEUE +#endif +#if defined(__linux__) +#define USE_SIGNALFD +#endif + +__RCSID("$NetBSD: rumpclient.c,v 1.65 2015/01/17 19:34:50 justin Exp $"); + +#include +#include +#include +#include + +#ifdef USE_KQUEUE +#include +#endif + +#include +#include +#include + +#include +#include +#include +#include +#include +#if defined(__minix) +#define _MTHREADIFY_PTHREADS +#include +#else +#include +#endif +#include +#include +#include +#include +#include +#include +#include + +#include + +#define HOSTOPS +int (*host_socket)(int, int, int); +int (*host_close)(int); +int (*host_connect)(int, const struct sockaddr *, socklen_t); +int (*host_fcntl)(int, int, ...); +int (*host_poll)(struct pollfd *, nfds_t, int); +ssize_t (*host_read)(int, void *, size_t); +ssize_t (*host_sendmsg)(int, const struct msghdr *, int); +int (*host_setsockopt)(int, int, int, const void *, socklen_t); +int (*host_dup)(int); + +#ifdef USE_KQUEUE +int (*host_kqueue)(void); +#ifdef __NetBSD__ +int (*host_kevent)(int, const struct kevent *, size_t, + struct kevent *, size_t, const struct timespec *); +#else +int (*host_kevent)(int, const struct kevent *, int, + struct kevent *, int, const struct timespec *); +#endif +#endif + +#ifdef USE_SIGNALFD +#include + +int (*host_signalfd)(int, const sigset_t *, int); +#endif + +int (*host_execve)(const char *, char *const[], char *const[]); + +#include "sp_common.c" +#include "rumpuser_sigtrans.c" + +static struct spclient clispc = { + .spc_fd = -1, +}; + +static int holyfd = -1; +static sigset_t fullset; + +static int doconnect(void); +static int handshake_req(struct spclient *, int, void *, int, bool); + +/* + * Default: don't retry. Most clients can't handle it + * (consider e.g. fds suddenly going missing). + */ +static time_t retrytimo = 0; + +/* always defined to nothingness for now */ +#define ERRLOG(a) + +static int +send_with_recon(struct spclient *spc, struct iovec *iov, size_t iovlen) +{ + struct timeval starttime, curtime; + time_t prevreconmsg; + unsigned reconretries; + int rv; + + for (prevreconmsg = 0, reconretries = 0;;) { + rv = dosend(spc, iov, iovlen); + if (__predict_false(rv == ENOTCONN || rv == EBADF)) { + /* no persistent connections */ + if (retrytimo == 0) { + rv = ENOTCONN; + break; + } + if (retrytimo == RUMPCLIENT_RETRYCONN_DIE) + _exit(1); + + if (!prevreconmsg) { + prevreconmsg = time(NULL); + gettimeofday(&starttime, NULL); + } + if (reconretries == 1) { + if (retrytimo == RUMPCLIENT_RETRYCONN_ONCE) { + rv = ENOTCONN; + break; + } + fprintf(stderr, "rump_sp: connection to " + "kernel lost, trying to reconnect ...\n"); + } else if (time(NULL) - prevreconmsg > 120) { + fprintf(stderr, "rump_sp: still trying to " + "reconnect ...\n"); + prevreconmsg = time(NULL); + } + + /* check that we aren't over the limit */ + if (retrytimo > 0) { + time_t tdiff; + + gettimeofday(&curtime, NULL); + tdiff = curtime.tv_sec - starttime.tv_sec; + if (starttime.tv_usec > curtime.tv_usec) + tdiff--; + if (tdiff >= retrytimo) { + fprintf(stderr, "rump_sp: reconnect " + "failed, %lld second timeout\n", + (long long)retrytimo); + return ENOTCONN; + } + } + + /* adhoc backoff timer */ + if (reconretries < 10) { + usleep(100000 * reconretries); + } else { + sleep(MIN(10, reconretries-9)); + } + reconretries++; + + if ((rv = doconnect()) != 0) + continue; + if ((rv = handshake_req(&clispc, HANDSHAKE_GUEST, + NULL, 0, true)) != 0) + continue; + + /* + * ok, reconnect succesful. we need to return to + * the upper layer to get the entire PDU resent. + */ + if (reconretries != 1) + fprintf(stderr, "rump_sp: reconnected!\n"); + rv = EAGAIN; + break; + } else { + _DIAGASSERT(errno != EAGAIN); + break; + } + } + + return rv; +} + +static int +cliwaitresp(struct spclient *spc, struct respwait *rw, sigset_t *mask, + bool keeplock) +{ + uint64_t mygen; + bool imalive = true; + + pthread_mutex_lock(&spc->spc_mtx); + if (!keeplock) + sendunlockl(spc); + mygen = spc->spc_generation; + + rw->rw_error = 0; + while (!rw->rw_done && rw->rw_error == 0) { + if (__predict_false(spc->spc_generation != mygen || !imalive)) + break; + + /* are we free to receive? */ + if (spc->spc_istatus == SPCSTATUS_FREE) { + int gotresp, dosig, rv; + + spc->spc_istatus = SPCSTATUS_BUSY; + pthread_mutex_unlock(&spc->spc_mtx); + + dosig = 0; + for (gotresp = 0; !gotresp; ) { +#ifdef USE_KQUEUE + struct kevent kev[8]; + int i; + + /* + * typically we don't have a frame waiting + * when we come in here, so call kevent now + */ + rv = host_kevent(holyfd, NULL, 0, + kev, __arraycount(kev), NULL); + + if (__predict_false(rv == -1)) { + goto activity; + } + + /* + * XXX: don't know how this can happen + * (timeout cannot expire since there + * isn't one), but it does happen. + * treat it as an expectional condition + * and go through tryread to determine + * alive status. + */ + if (__predict_false(rv == 0)) + goto activity; + + for (i = 0; i < rv; i++) { + if (kev[i].filter == EVFILT_SIGNAL) + dosig++; + } + if (dosig) + goto cleanup; + + /* + * ok, activity. try to read a frame to + * determine what happens next. + */ + activity: +#else /* !USE_KQUEUE */ + struct pollfd pfd[2]; + + 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 */ + + switch (readframe(spc)) { + case 0: + continue; + case -1: + imalive = false; + goto cleanup; + default: + /* case 1 */ + break; + } + + switch (spc->spc_hdr.rsp_class) { + case RUMPSP_RESP: + case RUMPSP_ERROR: + kickwaiter(spc); + gotresp = spc->spc_hdr.rsp_reqno == + rw->rw_reqno; + break; + case RUMPSP_REQ: + handlereq(spc); + break; + default: + /* panic */ + break; + } + } + + cleanup: + pthread_mutex_lock(&spc->spc_mtx); + if (spc->spc_istatus == SPCSTATUS_WANTED) + kickall(spc); + spc->spc_istatus = SPCSTATUS_FREE; + + /* take one for the team */ + if (dosig) { + pthread_mutex_unlock(&spc->spc_mtx); + pthread_sigmask(SIG_SETMASK, mask, NULL); + pthread_sigmask(SIG_SETMASK, &fullset, NULL); + pthread_mutex_lock(&spc->spc_mtx); + } + } else { + spc->spc_istatus = SPCSTATUS_WANTED; + pthread_cond_wait(&rw->rw_cv, &spc->spc_mtx); + } + } + TAILQ_REMOVE(&spc->spc_respwait, rw, rw_entries); + pthread_mutex_unlock(&spc->spc_mtx); + pthread_cond_destroy(&rw->rw_cv); + + if (spc->spc_generation != mygen || !imalive) { + return ENOTCONN; + } + return rw->rw_error; +} + +static int +syscall_req(struct spclient *spc, sigset_t *omask, int sysnum, + const void *data, size_t dlen, void **resp) +{ + struct rsp_hdr rhdr; + struct respwait rw; + struct iovec iov[2]; + int rv; + + rhdr.rsp_len = sizeof(rhdr) + dlen; + rhdr.rsp_class = RUMPSP_REQ; + rhdr.rsp_type = RUMPSP_SYSCALL; + rhdr.rsp_sysnum = sysnum; + + IOVPUT(iov[0], rhdr); + IOVPUT_WITHSIZE(iov[1], __UNCONST(data), dlen); + + do { + putwait(spc, &rw, &rhdr); + if ((rv = send_with_recon(spc, iov, __arraycount(iov))) != 0) { + unputwait(spc, &rw); + continue; + } + + rv = cliwaitresp(spc, &rw, omask, false); + if (rv == ENOTCONN) + rv = EAGAIN; + } while (rv == EAGAIN); + + *resp = rw.rw_data; + return rv; +} + +static int +handshake_req(struct spclient *spc, int type, void *data, + int cancel, bool haslock) +{ + struct handshake_fork rf; + const char *myprogname = NULL; /* XXXgcc */ + struct rsp_hdr rhdr; + struct respwait rw; + sigset_t omask; + size_t bonus; + struct iovec iov[2]; + int rv; + + if (type == HANDSHAKE_FORK) { + bonus = sizeof(rf); + } else { +#ifdef __NetBSD__ + /* would procfs work on NetBSD too? */ + myprogname = getprogname(); +#else + int fd = open("/proc/self/comm", O_RDONLY); + if (fd == -1) { + myprogname = "???"; + } else { + static char commname[128]; + + memset(commname, 0, sizeof(commname)); + if (read(fd, commname, sizeof(commname)) > 0) { + char *n; + + n = strrchr(commname, '\n'); + if (n) + *n = '\0'; + myprogname = commname; + } else { + myprogname = "???"; + } + close(fd); + } +#endif + bonus = strlen(myprogname)+1; + } + + /* performs server handshake */ + rhdr.rsp_len = sizeof(rhdr) + bonus; + rhdr.rsp_class = RUMPSP_REQ; + rhdr.rsp_type = RUMPSP_HANDSHAKE; + rhdr.rsp_handshake = type; + + IOVPUT(iov[0], rhdr); + + pthread_sigmask(SIG_SETMASK, &fullset, &omask); + if (haslock) + putwait_locked(spc, &rw, &rhdr); + else + putwait(spc, &rw, &rhdr); + if (type == HANDSHAKE_FORK) { + memcpy(rf.rf_auth, data, sizeof(rf.rf_auth)); /* uh, why? */ + rf.rf_cancel = cancel; + IOVPUT(iov[1], rf); + } else { + IOVPUT_WITHSIZE(iov[1], __UNCONST(myprogname), bonus); + } + rv = send_with_recon(spc, iov, __arraycount(iov)); + if (rv || cancel) { + if (haslock) + unputwait_locked(spc, &rw); + else + unputwait(spc, &rw); + if (cancel) { + goto out; + } + } else { + rv = cliwaitresp(spc, &rw, &omask, haslock); + } + if (rv) + goto out; + + rv = *(int *)rw.rw_data; + free(rw.rw_data); + + out: + pthread_sigmask(SIG_SETMASK, &omask, NULL); + return rv; +} + +static int +prefork_req(struct spclient *spc, sigset_t *omask, void **resp) +{ + struct rsp_hdr rhdr; + struct respwait rw; + struct iovec iov[1]; + int rv; + + rhdr.rsp_len = sizeof(rhdr); + rhdr.rsp_class = RUMPSP_REQ; + rhdr.rsp_type = RUMPSP_PREFORK; + rhdr.rsp_error = 0; + + IOVPUT(iov[0], rhdr); + + do { + putwait(spc, &rw, &rhdr); + rv = send_with_recon(spc, iov, __arraycount(iov)); + if (rv != 0) { + unputwait(spc, &rw); + continue; + } + + rv = cliwaitresp(spc, &rw, omask, false); + if (rv == ENOTCONN) + rv = EAGAIN; + } while (rv == EAGAIN); + + *resp = rw.rw_data; + return rv; +} + +/* + * prevent response code from deadlocking with reconnect code + */ +static int +resp_sendlock(struct spclient *spc) +{ + int rv = 0; + + pthread_mutex_lock(&spc->spc_mtx); + while (spc->spc_ostatus != SPCSTATUS_FREE) { + if (__predict_false(spc->spc_reconnecting)) { + rv = EBUSY; + goto out; + } + spc->spc_ostatus = SPCSTATUS_WANTED; + pthread_cond_wait(&spc->spc_cv, &spc->spc_mtx); + } + spc->spc_ostatus = SPCSTATUS_BUSY; + + out: + pthread_mutex_unlock(&spc->spc_mtx); + return rv; +} + +static void +send_copyin_resp(struct spclient *spc, uint64_t reqno, void *data, size_t dlen, + int wantstr) +{ + struct rsp_hdr rhdr; + struct iovec iov[2]; + + if (wantstr) + dlen = MIN(dlen, strlen(data)+1); + + rhdr.rsp_len = sizeof(rhdr) + dlen; + rhdr.rsp_reqno = reqno; + rhdr.rsp_class = RUMPSP_RESP; + rhdr.rsp_type = RUMPSP_COPYIN; + rhdr.rsp_sysnum = 0; + + IOVPUT(iov[0], rhdr); + IOVPUT_WITHSIZE(iov[1], data, dlen); + + if (resp_sendlock(spc) != 0) + return; + (void)SENDIOV(spc, iov); + sendunlock(spc); +} + +static void +send_anonmmap_resp(struct spclient *spc, uint64_t reqno, void *addr) +{ + struct rsp_hdr rhdr; + struct iovec iov[2]; + + rhdr.rsp_len = sizeof(rhdr) + sizeof(addr); + rhdr.rsp_reqno = reqno; + rhdr.rsp_class = RUMPSP_RESP; + rhdr.rsp_type = RUMPSP_ANONMMAP; + rhdr.rsp_sysnum = 0; + + IOVPUT(iov[0], rhdr); + IOVPUT(iov[1], addr); + + if (resp_sendlock(spc) != 0) + return; + (void)SENDIOV(spc, iov); + sendunlock(spc); +} + +int +rumpclient_syscall(int sysnum, const void *data, size_t dlen, + register_t *retval) +{ + struct rsp_sysresp *resp; + sigset_t omask; + void *rdata; + int rv; + + pthread_sigmask(SIG_SETMASK, &fullset, &omask); + + DPRINTF(("rumpsp syscall_req: syscall %d with %p/%zu\n", + sysnum, data, dlen)); + + rv = syscall_req(&clispc, &omask, sysnum, data, dlen, &rdata); + if (rv) + goto out; + + resp = rdata; + DPRINTF(("rumpsp syscall_resp: syscall %d error %d, rv: %d/%d\n", + sysnum, rv, resp->rsys_retval[0], resp->rsys_retval[1])); + + memcpy(retval, &resp->rsys_retval, sizeof(resp->rsys_retval)); + rv = resp->rsys_error; + free(rdata); + + out: + pthread_sigmask(SIG_SETMASK, &omask, NULL); + return rv; +} + +static void +handlereq(struct spclient *spc) +{ + struct rsp_copydata *copydata; + struct rsp_hdr *rhdr = &spc->spc_hdr; + void *mapaddr; + size_t maplen; + int reqtype = spc->spc_hdr.rsp_type; + int sig; + + switch (reqtype) { + case RUMPSP_COPYIN: + case RUMPSP_COPYINSTR: + /*LINTED*/ + copydata = (struct rsp_copydata *)spc->spc_buf; + DPRINTF(("rump_sp handlereq: copyin request: %p/%zu\n", + copydata->rcp_addr, copydata->rcp_len)); + send_copyin_resp(spc, spc->spc_hdr.rsp_reqno, + copydata->rcp_addr, copydata->rcp_len, + reqtype == RUMPSP_COPYINSTR); + break; + case RUMPSP_COPYOUT: + case RUMPSP_COPYOUTSTR: + /*LINTED*/ + copydata = (struct rsp_copydata *)spc->spc_buf; + DPRINTF(("rump_sp handlereq: copyout request: %p/%zu\n", + copydata->rcp_addr, copydata->rcp_len)); + /*LINTED*/ + memcpy(copydata->rcp_addr, copydata->rcp_data, + copydata->rcp_len); + break; + case RUMPSP_ANONMMAP: + /*LINTED*/ + maplen = *(size_t *)spc->spc_buf; + mapaddr = mmap(NULL, maplen, PROT_READ|PROT_WRITE, + MAP_ANON|MAP_PRIVATE, -1, 0); + if (mapaddr == MAP_FAILED) + mapaddr = NULL; + DPRINTF(("rump_sp handlereq: anonmmap: %p\n", mapaddr)); + send_anonmmap_resp(spc, spc->spc_hdr.rsp_reqno, mapaddr); + break; + case RUMPSP_RAISE: + sig = rumpuser__sig_rump2host(rhdr->rsp_signo); + DPRINTF(("rump_sp handlereq: raise sig %d\n", sig)); + raise(sig); + /* + * We most likely have signals blocked, but the signal + * will be handled soon enough when we return. + */ + break; + default: + printf("PANIC: INVALID TYPE %d\n", reqtype); + abort(); + break; + } + + spcfreebuf(spc); +} + +static unsigned ptab_idx; +static struct sockaddr *serv_sa; + +/* dup until we get a "good" fd which does not collide with stdio */ +static int +dupgood(int myfd, int mustchange) +{ + int ofds[4]; + int sverrno; + unsigned int i; + + for (i = 0; (myfd <= 2 || mustchange) && myfd != -1; i++) { + assert(i < __arraycount(ofds)); + ofds[i] = myfd; + myfd = host_dup(myfd); + if (mustchange) { + i--; /* prevent closing old fd */ + mustchange = 0; + } + } + + sverrno = 0; + if (myfd == -1 && i > 0) + sverrno = errno; + + while (i-- > 0) { + host_close(ofds[i]); + } + + if (sverrno) + errno = sverrno; + + 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 +doconnect(void) +{ + struct respwait rw; + struct rsp_hdr rhdr; + char banner[MAXBANNER]; + int s, error, flags; + ssize_t n; + + if (holyfd != -1) + host_close(holyfd); + holyfd = -1; + s = -1; + + if (clispc.spc_fd != -1) + host_close(clispc.spc_fd); + clispc.spc_fd = -1; + + /* + * for reconnect, gate everyone out of the receiver code + */ + putwait_locked(&clispc, &rw, &rhdr); + + pthread_mutex_lock(&clispc.spc_mtx); + clispc.spc_reconnecting = 1; + pthread_cond_broadcast(&clispc.spc_cv); + clispc.spc_generation++; + while (clispc.spc_istatus != SPCSTATUS_FREE) { + clispc.spc_istatus = SPCSTATUS_WANTED; + pthread_cond_wait(&rw.rw_cv, &clispc.spc_mtx); + } + kickall(&clispc); + + /* + * we can release it already since we hold the + * send lock during reconnect + * XXX: assert it + */ + clispc.spc_istatus = SPCSTATUS_FREE; + pthread_mutex_unlock(&clispc.spc_mtx); + unputwait_locked(&clispc, &rw); + + free(clispc.spc_buf); + clispc.spc_off = 0; + + s = dupgood(host_socket(parsetab[ptab_idx].domain, SOCK_STREAM, 0), 0); + if (s == -1) + return -1; + + while (host_connect(s, serv_sa, parsetab[ptab_idx].slen) == -1) { + if (errno == EINTR) + continue; + ERRLOG(("rump_sp: client connect failed: %s\n", + strerror(errno))); + return -1; + } + + if ((error = parsetab[ptab_idx].connhook(s)) != 0) { + ERRLOG(("rump_sp: connect hook failed\n")); + return -1; + } + + if ((n = host_read(s, banner, sizeof(banner)-1)) <= 0) { + ERRLOG(("rump_sp: failed to read banner\n")); + return -1; + } + + if (banner[n-1] != '\n') { + ERRLOG(("rump_sp: invalid banner\n")); + return -1; + } + banner[n] = '\0'; + /* XXX parse the banner some day */ + + flags = host_fcntl(s, F_GETFL, 0); + if (host_fcntl(s, F_SETFL, flags | O_NONBLOCK) == -1) { + ERRLOG(("rump_sp: socket fd NONBLOCK: %s\n", strerror(errno))); + return -1; + } + clispc.spc_fd = s; + clispc.spc_state = SPCSTATE_RUNNING; + clispc.spc_reconnecting = 0; + holyfd = makeholyfd(); + + return 0; +} + +static int +doinit(void) +{ + + TAILQ_INIT(&clispc.spc_respwait); + pthread_mutex_init(&clispc.spc_mtx, NULL); + pthread_cond_init(&clispc.spc_cv, NULL); + + return 0; +} + +#ifdef RTLD_NEXT +void *rumpclient__dlsym(void *, const char *); +void * +rumpclient__dlsym(void *handle, const char *symbol) +{ + + return dlsym(handle, symbol); +} +void *rumphijack_dlsym(void *, const char *) + __attribute__((__weak__, alias("rumpclient__dlsym"))); +#endif + +static pid_t init_done = 0; + +int +rumpclient_init(void) +{ + char *p; + int error; + int rv = -1; + int hstype; + pid_t mypid; + + /* + * Make sure we're not riding the context of a previous + * host fork. Note: it's *possible* that after n>1 forks + * we have the same pid as one of our exited parents, but + * I'm pretty sure there are 0 practical implications, since + * it means generations would have to skip rumpclient init. + */ + if (init_done == (mypid = getpid())) + return 0; + +#ifdef USE_KQUEUE + /* kq does not traverse fork() */ + holyfd = -1; +#endif + init_done = mypid; + + sigfillset(&fullset); + + /* + * sag mir, wo die symbols sind. zogen fort, der krieg beginnt. + * wann wird man je verstehen? wann wird man je verstehen? + */ +#ifdef RTLD_NEXT +#define FINDSYM2(_name_,_syscall_) \ + if ((host_##_name_ = rumphijack_dlsym(RTLD_NEXT, \ + #_syscall_)) == NULL) { \ + if (rumphijack_dlsym == rumpclient__dlsym) \ + host_##_name_ = _name_; /* static fallback */ \ + if (host_##_name_ == NULL) { \ + fprintf(stderr,"cannot find %s: %s", #_syscall_,\ + dlerror()); \ + exit(1); \ + } \ + } +#else +#define FINDSYM2(_name_,_syscall) \ + host_##_name_ = _name_; +#endif +#define FINDSYM(_name_) FINDSYM2(_name_,_name_) +#ifdef __NetBSD__ + FINDSYM2(socket,__socket30) +#else + FINDSYM(socket) +#endif + + FINDSYM(close) + FINDSYM(connect) + FINDSYM(fcntl) + FINDSYM(poll) + FINDSYM(read) + FINDSYM(sendmsg) + FINDSYM(setsockopt) + FINDSYM(dup) + FINDSYM(execve) + +#ifdef USE_KQUEUE + FINDSYM(kqueue) +#ifdef __NetBSD__ +#if !__NetBSD_Prereq__(5,99,7) + FINDSYM(kevent) +#else + FINDSYM2(kevent,_sys___kevent50) +#endif +#else + FINDSYM(kevent) +#endif +#endif /* USE_KQUEUE */ + +#ifdef USE_SIGNALFD + FINDSYM(signalfd) +#endif + +#undef FINDSYM +#undef FINDSY2 + + if ((p = getenv("RUMP__PARSEDSERVER")) == NULL) { + if ((p = getenv("RUMP_SERVER")) == NULL) { + fprintf(stderr, "error: RUMP_SERVER not set\n"); + errno = ENOENT; + goto out; + } + } + + if ((error = parseurl(p, &serv_sa, &ptab_idx, 0)) != 0) { + errno = error; + goto out; + } + + if (doinit() == -1) + goto out; + + if ((p = getenv("RUMPCLIENT__EXECFD")) != NULL) { + sscanf(p, "%d,%d", &clispc.spc_fd, &holyfd); + unsetenv("RUMPCLIENT__EXECFD"); + hstype = HANDSHAKE_EXEC; + } else { + if (doconnect() == -1) + goto out; + hstype = HANDSHAKE_GUEST; + } + + error = handshake_req(&clispc, hstype, NULL, 0, false); + if (error) { + pthread_mutex_destroy(&clispc.spc_mtx); + pthread_cond_destroy(&clispc.spc_cv); + if (clispc.spc_fd != -1) + host_close(clispc.spc_fd); + errno = error; + goto out; + } + rv = 0; + + out: + if (rv == -1) + init_done = 0; + return rv; +} + +struct rumpclient_fork { + uint32_t fork_auth[AUTHLEN]; + struct spclient fork_spc; + int fork_holyfd; +}; + +struct rumpclient_fork * +rumpclient_prefork(void) +{ + struct rumpclient_fork *rpf; + sigset_t omask; + void *resp; + int rv; + + pthread_sigmask(SIG_SETMASK, &fullset, &omask); + rpf = malloc(sizeof(*rpf)); + if (rpf == NULL) + goto out; + + if ((rv = prefork_req(&clispc, &omask, &resp)) != 0) { + free(rpf); + errno = rv; + rpf = NULL; + goto out; + } + + memcpy(rpf->fork_auth, resp, sizeof(rpf->fork_auth)); + free(resp); + + rpf->fork_spc = clispc; + rpf->fork_holyfd = holyfd; + + out: + pthread_sigmask(SIG_SETMASK, &omask, NULL); + return rpf; +} + +int +rumpclient_fork_init(struct rumpclient_fork *rpf) +{ + int error; + int osock; + + osock = clispc.spc_fd; + memset(&clispc, 0, sizeof(clispc)); + clispc.spc_fd = osock; + +#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) + return -1; + if (doconnect() == -1) + return -1; + + error = handshake_req(&clispc, HANDSHAKE_FORK, rpf->fork_auth, + 0, false); + if (error) { + pthread_mutex_destroy(&clispc.spc_mtx); + pthread_cond_destroy(&clispc.spc_cv); + errno = error; + return -1; + } + + return 0; +} + +/*ARGSUSED*/ +void +rumpclient_fork_cancel(struct rumpclient_fork *rpf) +{ + + /* EUNIMPL */ +} + +void +rumpclient_fork_vparent(struct rumpclient_fork *rpf) +{ + + clispc = rpf->fork_spc; + holyfd = rpf->fork_holyfd; +} + +void +rumpclient_setconnretry(time_t timeout) +{ + + if (timeout < RUMPCLIENT_RETRYCONN_DIE) + return; /* gigo */ + + retrytimo = timeout; +} + +int +rumpclient__closenotify(int *fdp, enum rumpclient_closevariant variant) +{ + int fd = *fdp; + int untilfd, rv; + int newfd; + + switch (variant) { + case RUMPCLIENT_CLOSE_FCLOSEM: + untilfd = MAX(clispc.spc_fd, holyfd); + for (; fd <= untilfd; fd++) { + if (fd == clispc.spc_fd || fd == holyfd) + continue; + rv = host_close(fd); + if (rv == -1) + return -1; + } + *fdp = fd; + break; + + case RUMPCLIENT_CLOSE_CLOSE: + case RUMPCLIENT_CLOSE_DUP2: + if (fd == clispc.spc_fd) { + newfd = dupgood(clispc.spc_fd, 1); + if (newfd == -1) + return -1; + +#ifdef USE_KQUEUE + { + struct kevent kev[2]; + + /* + * now, we have a new socket number, so change + * the file descriptor that kqueue is + * monitoring. remove old and add new. + */ + EV_SET(&kev[0], clispc.spc_fd, + EVFILT_READ, EV_DELETE, 0, 0, 0); + EV_SET(&kev[1], newfd, + EVFILT_READ, EV_ADD|EV_ENABLE, 0, 0, 0); + if (host_kevent(holyfd, kev, 2, NULL, 0, NULL) == -1) { + int sverrno = errno; + host_close(newfd); + errno = sverrno; + return -1; + }} +#endif /* !USE_KQUEUE */ + clispc.spc_fd = newfd; + } + if (holyfd != -1 && fd == holyfd) { + newfd = dupgood(holyfd, 1); + if (newfd == -1) + return -1; + holyfd = newfd; + } + break; + } + + return 0; +} + +pid_t +rumpclient_fork(void) +{ + + return rumpclient__dofork(fork); +} + +/* + * Process is about to exec. Save info about our existing connection + * in the env. rumpclient will check for this info in init(). + * This is mostly for the benefit of rumphijack, but regular applications + * may use it as well. + */ +int +rumpclient_exec(const char *path, char *const argv[], char *const envp[]) +{ + char buf[4096]; + char **newenv; + char *envstr, *envstr2; + size_t nelem; + int rv, sverrno; + + snprintf(buf, sizeof(buf), "RUMPCLIENT__EXECFD=%d,%d", + clispc.spc_fd, holyfd); + envstr = malloc(strlen(buf)+1); + if (envstr == NULL) { + return ENOMEM; + } + strcpy(envstr, buf); + + /* do we have a fully parsed url we want to forward in the env? */ + if (*parsedurl != '\0') { + snprintf(buf, sizeof(buf), + "RUMP__PARSEDSERVER=%s", parsedurl); + envstr2 = malloc(strlen(buf)+1); + if (envstr2 == NULL) { + free(envstr); + return ENOMEM; + } + strcpy(envstr2, buf); + } else { + envstr2 = NULL; + } + + for (nelem = 0; envp && envp[nelem]; nelem++) + continue; + + newenv = malloc(sizeof(*newenv) * (nelem+3)); + if (newenv == NULL) { + free(envstr2); + free(envstr); + return ENOMEM; + } + memcpy(&newenv[0], envp, nelem*sizeof(*envp)); + + newenv[nelem] = envstr; + newenv[nelem+1] = envstr2; + newenv[nelem+2] = NULL; + + rv = host_execve(path, argv, newenv); + + _DIAGASSERT(rv != 0); + sverrno = errno; + free(envstr2); + free(envstr); + free(newenv); + errno = sverrno; + return rv; +} + +/* + * daemon() is handwritten for the benefit of platforms which + * do not support daemon(). + */ +int +rumpclient_daemon(int nochdir, int noclose) +{ + struct rumpclient_fork *rf; + int sverrno; + + if ((rf = rumpclient_prefork()) == NULL) + return -1; + + switch (fork()) { + case 0: + break; + case -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) + return -1; + + return 0; + + daemonerr: + sverrno = errno; + rumpclient_fork_cancel(rf); + errno = sverrno; + return -1; +} diff --git a/lib/librumpclient/rumpclient.h b/lib/librumpclient/rumpclient.h new file mode 100644 index 000000000..8b5e6be92 --- /dev/null +++ b/lib/librumpclient/rumpclient.h @@ -0,0 +1,120 @@ +/* $NetBSD: rumpclient.h,v 1.15 2013/01/20 15:23:21 pooka Exp $ */ + +/*- + * Copyright (c) 2010 Antti Kantee. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef _RUMP_RUMPCLIENT_H_ +#define _RUMP_RUMPCLIENT_H_ + +#include + +#if !defined(__returns_twice) +#ifdef __GNUC__ +#define __returns_twice __attribute__((__returns_twice__)) +#else /* __GNUC__ */ +#define __returns_twice +#endif /* !__GNUC__ */ +#endif /* !__returns_twice */ + +#if defined(__sun__) && !defined(RUMP_REGISTER_T) +#define RUMP_REGISTER_T long +typedef RUMP_REGISTER_T register_t; +#endif + +struct rumpclient_fork; + +#define rumpclient_vfork() rumpclient__dofork(vfork) + +#ifdef __BEGIN_DECLS +__BEGIN_DECLS +#elif defined(__cplusplus) +extern "C" { +#endif + +int rumpclient_syscall(int, const void *, size_t, register_t *); +int rumpclient_init(void); + +struct rumpclient_fork *rumpclient_prefork(void); +int rumpclient_fork_init(struct rumpclient_fork *); +void rumpclient_fork_cancel(struct rumpclient_fork *); +void rumpclient_fork_vparent(struct rumpclient_fork *); + +pid_t rumpclient_fork(void); +int rumpclient_exec(const char *, char *const [], char *const[]); +int rumpclient_daemon(int, int); + +#define RUMPCLIENT_RETRYCONN_INFTIME ((time_t)-1) +#define RUMPCLIENT_RETRYCONN_ONCE ((time_t)-2) +#define RUMPCLIENT_RETRYCONN_DIE ((time_t)-3) +void rumpclient_setconnretry(time_t); + +enum rumpclient_closevariant { + RUMPCLIENT_CLOSE_CLOSE, + RUMPCLIENT_CLOSE_DUP2, + RUMPCLIENT_CLOSE_FCLOSEM +}; +int rumpclient__closenotify(int *, enum rumpclient_closevariant); + + +/* + * vfork needs to be implemented as an inline to make everything + * run in the caller's stackframe. + */ +static __attribute__((__always_inline__)) __returns_twice inline pid_t +rumpclient__dofork(pid_t (*forkfn)(void)) +{ + struct rumpclient_fork *rf; + pid_t pid; + int childran = 0; + + if (!(rf = rumpclient_prefork())) + return -1; + + switch ((pid = forkfn())) { + case -1: + rumpclient_fork_cancel(rf); + break; + case 0: + childran = 1; + if (rumpclient_fork_init(rf) == -1) + pid = -1; + break; + default: + /* XXX: multithreaded vforker? do they exist? */ + if (childran) + rumpclient_fork_vparent(rf); + break; + } + + return pid; +} + +#ifdef __END_DECLS +__END_DECLS +#elif defined(__cplusplus) +} +#endif + +#endif /* _RUMP_RUMPCLIENT_H_ */ diff --git a/lib/librumpclient/shlib_version b/lib/librumpclient/shlib_version new file mode 100644 index 000000000..504280ef1 --- /dev/null +++ b/lib/librumpclient/shlib_version @@ -0,0 +1,4 @@ +# $NetBSD: shlib_version,v 1.1 2010/11/04 21:01:29 pooka Exp $ +# +major=0 +minor=0 diff --git a/lib/librumpuser/sp_common.c b/lib/librumpuser/sp_common.c index 6273e89a0..7c37c3233 100644 --- a/lib/librumpuser/sp_common.c +++ b/lib/librumpuser/sp_common.c @@ -48,7 +48,12 @@ #include #include #include +#if defined(__minix) +#define _MTHREADIFY_PTHREADS +#include +#else #include +#endif #include #include #include