Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files. =================================================================== RCS file: /ftp/cvs/cvsroot/src/lib/librumpclient/rumpclient.c,v rcsdiff: /ftp/cvs/cvsroot/src/lib/librumpclient/rumpclient.c,v: warning: Unknown phrases like `commitid ...;' are present. retrieving revision 1.32 retrieving revision 1.45.4.4 diff -u -p -r1.32 -r1.45.4.4 --- src/lib/librumpclient/rumpclient.c 2011/02/16 19:26:58 1.32 +++ src/lib/librumpclient/rumpclient.c 2014/05/22 11:37:00 1.45.4.4 @@ -1,4 +1,4 @@ -/* $NetBSD: rumpclient.c,v 1.32 2011/02/16 19:26:58 pooka Exp $ */ +/* $NetBSD: rumpclient.c,v 1.45.4.4 2014/05/22 11:37:00 yamt Exp $ */ /* * Copyright (c) 2010, 2011 Antti Kantee. All Rights Reserved. @@ -29,13 +29,37 @@ * Client side routines for rump syscall proxy. */ -#include -__RCSID("$NetBSD"); +#include + +/* + * We use kqueue on NetBSD, poll elsewhere. Theoretically we could + * use kqueue on other BSD's too, but I haven't tested those. 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. + */ + +#ifdef __NetBSD__ +#define USE_KQUEUE +#endif +#if defined(__linux__) && !defined(__ANDROID__) +#define USE_SIGNALFD +#endif + +__RCSID("$NetBSD: rumpclient.c,v 1.45.4.4 2014/05/22 11:37:00 yamt Exp $"); #include -#include #include #include +#include + +#ifdef USE_KQUEUE +#include +#endif #include #include @@ -45,7 +69,6 @@ __RCSID("$NetBSD"); #include #include #include -#include #include #include #include @@ -63,29 +86,45 @@ int (*host_socket)(int, int, int); int (*host_close)(int); int (*host_connect)(int, const struct sockaddr *, socklen_t); int (*host_fcntl)(int, int, ...); +#ifdef __ANDROID__ +int (*host_poll)(struct pollfd *, nfds_t, long); +#else int (*host_poll)(struct pollfd *, nfds_t, int); +#endif ssize_t (*host_read)(int, void *, size_t); -ssize_t (*host_sendto)(int, const void *, size_t, int, - const struct sockaddr *, socklen_t); +#ifdef __ANDROID__ +int (*host_sendmsg)(int, const struct msghdr *, unsigned int); +#else +ssize_t (*host_sendmsg)(int, const struct msghdr *, int); +#endif int (*host_setsockopt)(int, int, int, const void *, socklen_t); int (*host_dup)(int); +#ifdef USE_KQUEUE int (*host_kqueue)(void); int (*host_kevent)(int, const struct kevent *, size_t, struct kevent *, size_t, const struct timespec *); +#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 kq = -1; +static int holyfd = -1; static sigset_t fullset; -static int doconnect(bool); +static int doconnect(void); static int handshake_req(struct spclient *, int, void *, int, bool); /* @@ -94,8 +133,11 @@ static int handshake_req(struct spclient */ static time_t retrytimo = 0; +/* always defined to nothingness for now */ +#define ERRLOG(a) + static int -send_with_recon(struct spclient *spc, const void *data, size_t dlen) +send_with_recon(struct spclient *spc, struct iovec *iov, size_t iovlen) { struct timeval starttime, curtime; time_t prevreconmsg; @@ -103,7 +145,7 @@ send_with_recon(struct spclient *spc, co int rv; for (prevreconmsg = 0, reconretries = 0;;) { - rv = dosend(spc, data, dlen); + rv = dosend(spc, iov, iovlen); if (__predict_false(rv == ENOTCONN || rv == EBADF)) { /* no persistent connections */ if (retrytimo == 0) { @@ -111,7 +153,7 @@ send_with_recon(struct spclient *spc, co break; } if (retrytimo == RUMPCLIENT_RETRYCONN_DIE) - exit(1); + _exit(1); if (!prevreconmsg) { prevreconmsg = time(NULL); @@ -132,11 +174,13 @@ send_with_recon(struct spclient *spc, co /* check that we aren't over the limit */ if (retrytimo > 0) { - struct timeval tmp; + time_t tdiff; gettimeofday(&curtime, NULL); - timersub(&curtime, &starttime, &tmp); - if (tmp.tv_sec >= retrytimo) { + 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); @@ -152,7 +196,7 @@ send_with_recon(struct spclient *spc, co } reconretries++; - if ((rv = doconnect(false)) != 0) + if ((rv = doconnect()) != 0) continue; if ((rv = handshake_req(&clispc, HANDSHAKE_GUEST, NULL, 0, true)) != 0) @@ -194,45 +238,74 @@ cliwaitresp(struct spclient *spc, struct /* are we free to receive? */ if (spc->spc_istatus == SPCSTATUS_FREE) { - struct kevent kev[8]; - int gotresp, dosig, rv, i; + int gotresp, dosig, rv; spc->spc_istatus = SPCSTATUS_BUSY; pthread_mutex_unlock(&spc->spc_mtx); dosig = 0; for (gotresp = 0; !gotresp; ) { - switch (readframe(spc)) { - case 0: - rv = host_kevent(kq, NULL, 0, - kev, __arraycount(kev), NULL); +#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; - if (__predict_false(rv == -1)) { - goto cleanup; - } - - /* - * XXX: don't know how this can - * happen (timeout cannot expire - * since there isn't one), but - * it does happen - */ - if (__predict_false(rv == 0)) - continue; - - 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; } @@ -286,6 +359,7 @@ syscall_req(struct spclient *spc, sigset { struct rsp_hdr rhdr; struct respwait rw; + struct iovec iov[2]; int rv; rhdr.rsp_len = sizeof(rhdr) + dlen; @@ -293,13 +367,12 @@ syscall_req(struct spclient *spc, sigset 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, &rhdr, sizeof(rhdr))) != 0) { - unputwait(spc, &rw); - continue; - } - if ((rv = send_with_recon(spc, data, dlen)) != 0) { + if ((rv = send_with_recon(spc, iov, __arraycount(iov))) != 0) { unputwait(spc, &rw); continue; } @@ -318,16 +391,42 @@ handshake_req(struct spclient *spc, int 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 { - bonus = strlen(getprogname())+1; +#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 */ @@ -336,19 +435,21 @@ handshake_req(struct spclient *spc, int 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); - rv = dosend(spc, &rhdr, sizeof(rhdr)); if (type == HANDSHAKE_FORK) { memcpy(rf.rf_auth, data, sizeof(rf.rf_auth)); /* uh, why? */ rf.rf_cancel = cancel; - rv = send_with_recon(spc, &rf, sizeof(rf)); + IOVPUT(iov[1], rf); } else { - rv = dosend(spc, getprogname(), strlen(getprogname())+1); + IOVPUT_WITHSIZE(iov[1], __UNCONST(myprogname), bonus); } + rv = send_with_recon(spc, iov, __arraycount(iov)); if (rv || cancel) { if (haslock) unputwait_locked(spc, &rw); @@ -376,6 +477,7 @@ prefork_req(struct spclient *spc, sigset { struct rsp_hdr rhdr; struct respwait rw; + struct iovec iov[1]; int rv; rhdr.rsp_len = sizeof(rhdr); @@ -383,9 +485,11 @@ prefork_req(struct spclient *spc, sigset rhdr.rsp_type = RUMPSP_PREFORK; rhdr.rsp_error = 0; + IOVPUT(iov[0], rhdr); + do { putwait(spc, &rw, &rhdr); - rv = send_with_recon(spc, &rhdr, sizeof(rhdr)); + rv = send_with_recon(spc, iov, __arraycount(iov)); if (rv != 0) { unputwait(spc, &rw); continue; @@ -429,6 +533,7 @@ send_copyin_resp(struct spclient *spc, u int wantstr) { struct rsp_hdr rhdr; + struct iovec iov[2]; if (wantstr) dlen = MIN(dlen, strlen(data)+1); @@ -439,10 +544,12 @@ send_copyin_resp(struct spclient *spc, u 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)dosend(spc, &rhdr, sizeof(rhdr)); - (void)dosend(spc, data, dlen); + (void)SENDIOV(spc, iov); sendunlock(spc); } @@ -450,6 +557,7 @@ 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; @@ -457,10 +565,12 @@ send_anonmmap_resp(struct spclient *spc, rhdr.rsp_type = RUMPSP_ANONMMAP; rhdr.rsp_sysnum = 0; + IOVPUT(iov[0], rhdr); + IOVPUT(iov[1], addr); + if (resp_sendlock(spc) != 0) return; - (void)dosend(spc, &rhdr, sizeof(rhdr)); - (void)dosend(spc, &addr, sizeof(addr)); + (void)SENDIOV(spc, iov); sendunlock(spc); } @@ -503,6 +613,7 @@ handlereq(struct spclient *spc) void *mapaddr; size_t maplen; int reqtype = spc->spc_hdr.rsp_type; + int sig; switch (reqtype) { case RUMPSP_COPYIN: @@ -529,15 +640,16 @@ handlereq(struct spclient *spc) /*LINTED*/ maplen = *(size_t *)spc->spc_buf; mapaddr = mmap(NULL, maplen, PROT_READ|PROT_WRITE, - MAP_ANON, -1, 0); + 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: - DPRINTF(("rump_sp handlereq: raise sig %d\n", rhdr->rsp_signo)); - raise((int)rhdr->rsp_signo); + 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. @@ -560,7 +672,8 @@ static int dupgood(int myfd, int mustchange) { int ofds[4]; - int i; + int sverrno; + unsigned int i; for (i = 0; (myfd <= 2 || mustchange) && myfd != -1; i++) { assert(i < __arraycount(ofds)); @@ -572,27 +685,80 @@ dupgood(int myfd, int mustchange) } } - for (i--; i >= 0; i--) { + 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 -doconnect(bool noisy) +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; - struct kevent kev[NSIG+1]; char banner[MAXBANNER]; - struct pollfd pfd; - int s, error, flags, i; + int s, error, flags; ssize_t n; - if (kq != -1) - host_close(kq); - kq = -1; + if (holyfd != -1) + host_close(holyfd); + holyfd = -1; s = -1; if (clispc.spc_fd != -1) @@ -630,77 +796,40 @@ doconnect(bool noisy) if (s == -1) return -1; - pfd.fd = s; - pfd.events = POLLIN; - while (host_connect(s, serv_sa, (socklen_t)serv_sa->sa_len) == -1) { + while (host_connect(s, serv_sa, parsetab[ptab_idx].slen) == -1) { if (errno == EINTR) continue; - error = errno; - if (noisy) - fprintf(stderr, "rump_sp: client connect failed: %s\n", - strerror(errno)); - errno = error; + ERRLOG(("rump_sp: client connect failed: %s\n", + strerror(errno))); return -1; } if ((error = parsetab[ptab_idx].connhook(s)) != 0) { - error = errno; - if (noisy) - fprintf(stderr, "rump_sp: connect hook failed\n"); - errno = error; + ERRLOG(("rump_sp: connect hook failed\n")); return -1; } - if ((n = host_read(s, banner, sizeof(banner)-1)) < 0) { - error = errno; - if (noisy) - fprintf(stderr, "rump_sp: failed to read banner\n"); - errno = error; + 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') { - if (noisy) - fprintf(stderr, "rump_sp: invalid banner\n"); - errno = EINVAL; + ERRLOG(("rump_sp: invalid banner\n")); return -1; } banner[n] = '\0'; - /* parse the banner some day */ + /* XXX parse the banner some day */ flags = host_fcntl(s, F_GETFL, 0); if (host_fcntl(s, F_SETFL, flags | O_NONBLOCK) == -1) { - if (noisy) - fprintf(stderr, "rump_sp: socket fd NONBLOCK: %s\n", - strerror(errno)); - errno = EINVAL; + 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; - - /* setup kqueue, we want all signals and the fd */ - if ((kq = dupgood(host_kqueue(), 0)) == -1) { - error = errno; - if (noisy) - fprintf(stderr, "rump_sp: cannot setup kqueue"); - errno = error; - 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) { - error = errno; - if (noisy) - fprintf(stderr, "rump_sp: kevent() failed"); - errno = error; - return -1; - } + holyfd = makeholyfd(); return 0; } @@ -716,57 +845,103 @@ doinit(void) return 0; } -void *(*rumpclient_dlsym)(void *, const char *); -static int init_done = 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() +rumpclient_init(void) { char *p; int error; int rv = -1; int hstype; + pid_t mypid; - if (init_done) + /* + * 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; - init_done = 1; - sigfillset(&fullset); +#ifdef USE_KQUEUE + /* kq does not traverse fork() */ + holyfd = -1; +#endif + init_done = mypid; - /* dlsym overrided by rumphijack? */ - if (!rumpclient_dlsym) - rumpclient_dlsym = dlsym; + sigfillset(&fullset); /* - * sag mir, wo die symbol 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? */ +#ifdef RTLD_NEXT #define FINDSYM2(_name_,_syscall_) \ - if ((host_##_name_ = rumpclient_dlsym(RTLD_NEXT, \ - #_syscall_)) == NULL) \ - /* host_##_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_) - FINDSYM2(socket,__socket30); - FINDSYM(close); - FINDSYM(connect); - FINDSYM(fcntl); - FINDSYM(poll); - FINDSYM(read); - FINDSYM(sendto); - FINDSYM(setsockopt); - FINDSYM(dup); - FINDSYM(kqueue); - FINDSYM(execve); +#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) #if !__NetBSD_Prereq__(5,99,7) - FINDSYM(kevent); + FINDSYM(kevent) #else - FINDSYM2(kevent,_sys___kevent50); + FINDSYM2(kevent,_sys___kevent50) #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; } @@ -781,11 +956,11 @@ rumpclient_init() goto out; 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"); hstype = HANDSHAKE_EXEC; } else { - if (doconnect(true) == -1) + if (doconnect() == -1) goto out; hstype = HANDSHAKE_GUEST; } @@ -810,7 +985,7 @@ rumpclient_init() struct rumpclient_fork { uint32_t fork_auth[AUTHLEN]; struct spclient fork_spc; - int fork_kq; + int fork_holyfd; }; struct rumpclient_fork * @@ -837,7 +1012,7 @@ rumpclient_prefork(void) free(resp); rpf->fork_spc = clispc; - rpf->fork_kq = kq; + rpf->fork_holyfd = holyfd; out: pthread_sigmask(SIG_SETMASK, &omask, NULL); @@ -854,11 +1029,18 @@ rumpclient_fork_init(struct rumpclient_f memset(&clispc, 0, sizeof(clispc)); 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) return -1; - if (doconnect(false) == -1) + if (doconnect() == -1) return -1; error = handshake_req(&clispc, HANDSHAKE_FORK, rpf->fork_auth, @@ -873,6 +1055,7 @@ rumpclient_fork_init(struct rumpclient_f return 0; } +/*ARGSUSED*/ void rumpclient_fork_cancel(struct rumpclient_fork *rpf) { @@ -885,7 +1068,7 @@ rumpclient_fork_vparent(struct rumpclien { clispc = rpf->fork_spc; - kq = rpf->fork_kq; + holyfd = rpf->fork_holyfd; } void @@ -907,9 +1090,9 @@ rumpclient__closenotify(int *fdp, enum r switch (variant) { case RUMPCLIENT_CLOSE_FCLOSEM: - untilfd = MAX(clispc.spc_fd, kq); + untilfd = MAX(clispc.spc_fd, holyfd); for (; fd <= untilfd; fd++) { - if (fd == clispc.spc_fd || fd == kq) + if (fd == clispc.spc_fd || fd == holyfd) continue; rv = host_close(fd); if (rv == -1) @@ -921,11 +1104,14 @@ rumpclient__closenotify(int *fdp, enum r case RUMPCLIENT_CLOSE_CLOSE: case RUMPCLIENT_CLOSE_DUP2: if (fd == clispc.spc_fd) { - struct kevent kev[2]; - 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 @@ -935,19 +1121,20 @@ rumpclient__closenotify(int *fdp, enum r EVFILT_READ, EV_DELETE, 0, 0, 0); EV_SET(&kev[1], newfd, 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; host_close(newfd); errno = sverrno; return -1; - } + }} +#endif /* !USE_KQUEUE */ clispc.spc_fd = newfd; } - if (fd == kq) { - newfd = dupgood(kq, 1); + if (holyfd != -1 && fd == holyfd) { + newfd = dupgood(holyfd, 1); if (newfd == -1) return -1; - kq = newfd; + holyfd = newfd; } break; } @@ -956,7 +1143,7 @@ rumpclient__closenotify(int *fdp, enum r } pid_t -rumpclient_fork() +rumpclient_fork(void) { return rumpclient__dofork(fork); @@ -978,7 +1165,7 @@ rumpclient_exec(const char *path, char * int rv, sverrno; snprintf(buf, sizeof(buf), "RUMPCLIENT__EXECFD=%d,%d", - clispc.spc_fd, kq); + clispc.spc_fd, holyfd); envstr = malloc(strlen(buf)+1); if (envstr == NULL) { return ENOMEM; @@ -1002,7 +1189,7 @@ rumpclient_exec(const char *path, char * for (nelem = 0; envp && envp[nelem]; nelem++) continue; - newenv = malloc(sizeof(*newenv) * nelem+3); + newenv = malloc(sizeof(*newenv) * (nelem+3)); if (newenv == NULL) { free(envstr2); free(envstr); @@ -1025,6 +1212,10 @@ rumpclient_exec(const char *path, char * return rv; } +/* + * daemon() is handwritten for the benefit of platforms which + * do not support daemon(). + */ int rumpclient_daemon(int nochdir, int noclose) { @@ -1034,15 +1225,37 @@ rumpclient_daemon(int nochdir, int noclo if ((rf = rumpclient_prefork()) == NULL) return -1; - if (daemon(nochdir, noclose) == -1) { - sverrno = errno; - rumpclient_fork_cancel(rf); - errno = sverrno; - 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; }