xref: /netbsd/lib/librumpclient/rumpclient.c (revision 6550d01e)
1 /*      $NetBSD: rumpclient.c,v 1.22 2011/02/05 12:38:19 pooka Exp $	*/
2 
3 /*
4  * Copyright (c) 2010, 2011 Antti Kantee.  All Rights Reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
16  * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18  * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
21  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  */
27 
28 /*
29  * Client side routines for rump syscall proxy.
30  */
31 
32 #include <sys/cdefs.h>
33 __RCSID("$NetBSD");
34 
35 #include <sys/param.h>
36 #include <sys/event.h>
37 #include <sys/mman.h>
38 #include <sys/socket.h>
39 
40 #include <arpa/inet.h>
41 #include <netinet/in.h>
42 #include <netinet/tcp.h>
43 
44 #include <assert.h>
45 #include <dlfcn.h>
46 #include <errno.h>
47 #include <fcntl.h>
48 #include <link.h>
49 #include <poll.h>
50 #include <pthread.h>
51 #include <signal.h>
52 #include <stdarg.h>
53 #include <stdbool.h>
54 #include <stdio.h>
55 #include <stdlib.h>
56 #include <string.h>
57 #include <unistd.h>
58 
59 #include <rump/rumpclient.h>
60 
61 #define HOSTOPS
62 int	(*host_socket)(int, int, int);
63 int	(*host_close)(int);
64 int	(*host_connect)(int, const struct sockaddr *, socklen_t);
65 int	(*host_fcntl)(int, int, ...);
66 int	(*host_poll)(struct pollfd *, nfds_t, int);
67 ssize_t	(*host_read)(int, void *, size_t);
68 ssize_t (*host_sendto)(int, const void *, size_t, int,
69 		       const struct sockaddr *, socklen_t);
70 int	(*host_setsockopt)(int, int, int, const void *, socklen_t);
71 
72 int	(*host_kqueue)(void);
73 int	(*host_kevent)(int, const struct kevent *, size_t,
74 		       struct kevent *, size_t, const struct timespec *);
75 
76 #include "sp_common.c"
77 
78 static struct spclient clispc = {
79 	.spc_fd = -1,
80 };
81 
82 static int kq = -1;
83 static sigset_t fullset;
84 
85 static int doconnect(bool);
86 static int handshake_req(struct spclient *, uint32_t *, int, bool);
87 
88 time_t retrytimo = RUMPCLIENT_RETRYCONN_ONCE;
89 
90 static int
91 send_with_recon(struct spclient *spc, const void *data, size_t dlen)
92 {
93 	struct timeval starttime, curtime;
94 	time_t prevreconmsg;
95 	unsigned reconretries;
96 	int rv;
97 
98 	for (prevreconmsg = 0, reconretries = 0;;) {
99 		rv = dosend(spc, data, dlen);
100 		if (__predict_false(rv == ENOTCONN || rv == EBADF)) {
101 			/* no persistent connections */
102 			if (retrytimo == 0)
103 				break;
104 
105 			if (!prevreconmsg) {
106 				prevreconmsg = time(NULL);
107 				gettimeofday(&starttime, NULL);
108 			}
109 			if (reconretries == 1) {
110 				if (retrytimo == RUMPCLIENT_RETRYCONN_ONCE) {
111 					rv = ENOTCONN;
112 					break;
113 				}
114 				fprintf(stderr, "rump_sp: connection to "
115 				    "kernel lost, trying to reconnect ...\n");
116 			} else if (time(NULL) - prevreconmsg > 120) {
117 				fprintf(stderr, "rump_sp: still trying to "
118 				    "reconnect ...\n");
119 				prevreconmsg = time(NULL);
120 			}
121 
122 			/* check that we aren't over the limit */
123 			if (retrytimo > 0) {
124 				struct timeval tmp;
125 
126 				gettimeofday(&curtime, NULL);
127 				timersub(&curtime, &starttime, &tmp);
128 				if (tmp.tv_sec >= retrytimo) {
129 					fprintf(stderr, "rump_sp: reconnect "
130 					    "failed, %lld second timeout\n",
131 					    (long long)retrytimo);
132 					return ENOTCONN;
133 				}
134 			}
135 
136 			/* adhoc backoff timer */
137 			if (reconretries < 10) {
138 				usleep(100000 * reconretries);
139 			} else {
140 				sleep(MIN(10, reconretries-9));
141 			}
142 			reconretries++;
143 
144 			if ((rv = doconnect(false)) != 0)
145 				continue;
146 			if ((rv = handshake_req(&clispc, NULL, 0, true)) != 0)
147 				continue;
148 
149 			/*
150 			 * ok, reconnect succesful.  we need to return to
151 			 * the upper layer to get the entire PDU resent.
152 			 */
153 			if (reconretries != 1)
154 				fprintf(stderr, "rump_sp: reconnected!\n");
155 			rv = EAGAIN;
156 			break;
157 		} else {
158 			_DIAGASSERT(errno != EAGAIN);
159 			break;
160 		}
161 	}
162 
163 	return rv;
164 }
165 
166 static int
167 cliwaitresp(struct spclient *spc, struct respwait *rw, sigset_t *mask,
168 	bool keeplock)
169 {
170 	uint64_t mygen;
171 	bool imalive = true;
172 
173 	pthread_mutex_lock(&spc->spc_mtx);
174 	if (!keeplock)
175 		sendunlockl(spc);
176 	mygen = spc->spc_generation;
177 
178 	rw->rw_error = 0;
179 	while (!rw->rw_done && rw->rw_error == 0) {
180 		if (__predict_false(spc->spc_generation != mygen || !imalive))
181 			break;
182 
183 		/* are we free to receive? */
184 		if (spc->spc_istatus == SPCSTATUS_FREE) {
185 			struct kevent kev[8];
186 			int gotresp, dosig, rv, i;
187 
188 			spc->spc_istatus = SPCSTATUS_BUSY;
189 			pthread_mutex_unlock(&spc->spc_mtx);
190 
191 			dosig = 0;
192 			for (gotresp = 0; !gotresp; ) {
193 				switch (readframe(spc)) {
194 				case 0:
195 					rv = host_kevent(kq, NULL, 0,
196 					    kev, __arraycount(kev), NULL);
197 
198 					/*
199 					 * XXX: don't know how this can
200 					 * happen (timeout cannot expire
201 					 * since there isn't one), but
202 					 * it does happen
203 					 */
204 					if (__predict_false(rv == 0))
205 						continue;
206 
207 					for (i = 0; i < rv; i++) {
208 						if (kev[i].filter
209 						    == EVFILT_SIGNAL)
210 							dosig++;
211 					}
212 					if (dosig)
213 						goto cleanup;
214 
215 					continue;
216 				case -1:
217 					imalive = false;
218 					goto cleanup;
219 				default:
220 					break;
221 				}
222 
223 				switch (spc->spc_hdr.rsp_class) {
224 				case RUMPSP_RESP:
225 				case RUMPSP_ERROR:
226 					kickwaiter(spc);
227 					gotresp = spc->spc_hdr.rsp_reqno ==
228 					    rw->rw_reqno;
229 					break;
230 				case RUMPSP_REQ:
231 					handlereq(spc);
232 					break;
233 				default:
234 					/* panic */
235 					break;
236 				}
237 			}
238 
239  cleanup:
240 			pthread_mutex_lock(&spc->spc_mtx);
241 			if (spc->spc_istatus == SPCSTATUS_WANTED)
242 				kickall(spc);
243 			spc->spc_istatus = SPCSTATUS_FREE;
244 
245 			/* take one for the team */
246 			if (dosig) {
247 				pthread_mutex_unlock(&spc->spc_mtx);
248 				pthread_sigmask(SIG_SETMASK, mask, NULL);
249 				pthread_sigmask(SIG_SETMASK, &fullset, NULL);
250 				pthread_mutex_lock(&spc->spc_mtx);
251 			}
252 		} else {
253 			spc->spc_istatus = SPCSTATUS_WANTED;
254 			pthread_cond_wait(&rw->rw_cv, &spc->spc_mtx);
255 		}
256 	}
257 	TAILQ_REMOVE(&spc->spc_respwait, rw, rw_entries);
258 	pthread_mutex_unlock(&spc->spc_mtx);
259 	pthread_cond_destroy(&rw->rw_cv);
260 
261 	if (spc->spc_generation != mygen || !imalive) {
262 		return ENOTCONN;
263 	}
264 	return rw->rw_error;
265 }
266 
267 static int
268 syscall_req(struct spclient *spc, int sysnum,
269 	const void *data, size_t dlen, void **resp)
270 {
271 	struct rsp_hdr rhdr;
272 	struct respwait rw;
273 	sigset_t omask;
274 	int rv;
275 
276 	rhdr.rsp_len = sizeof(rhdr) + dlen;
277 	rhdr.rsp_class = RUMPSP_REQ;
278 	rhdr.rsp_type = RUMPSP_SYSCALL;
279 	rhdr.rsp_sysnum = sysnum;
280 
281 	pthread_sigmask(SIG_SETMASK, &fullset, &omask);
282 	do {
283 		putwait(spc, &rw, &rhdr);
284 		if ((rv = send_with_recon(spc, &rhdr, sizeof(rhdr))) != 0) {
285 			unputwait(spc, &rw);
286 			continue;
287 		}
288 		if ((rv = send_with_recon(spc, data, dlen)) != 0) {
289 			unputwait(spc, &rw);
290 			continue;
291 		}
292 
293 		rv = cliwaitresp(spc, &rw, &omask, false);
294 		if (rv == ENOTCONN)
295 			rv = EAGAIN;
296 	} while (rv == EAGAIN);
297 	pthread_sigmask(SIG_SETMASK, &omask, NULL);
298 
299 	*resp = rw.rw_data;
300 	return rv;
301 }
302 
303 static int
304 handshake_req(struct spclient *spc, uint32_t *auth, int cancel, bool haslock)
305 {
306 	struct handshake_fork rf;
307 	struct rsp_hdr rhdr;
308 	struct respwait rw;
309 	sigset_t omask;
310 	size_t bonus;
311 	int rv;
312 
313 	if (auth) {
314 		bonus = sizeof(rf);
315 	} else {
316 		bonus = strlen(getprogname())+1;
317 	}
318 
319 	/* performs server handshake */
320 	rhdr.rsp_len = sizeof(rhdr) + bonus;
321 	rhdr.rsp_class = RUMPSP_REQ;
322 	rhdr.rsp_type = RUMPSP_HANDSHAKE;
323 	if (auth)
324 		rhdr.rsp_handshake = HANDSHAKE_FORK;
325 	else
326 		rhdr.rsp_handshake = HANDSHAKE_GUEST;
327 
328 	pthread_sigmask(SIG_SETMASK, &fullset, &omask);
329 	if (haslock)
330 		putwait_locked(spc, &rw, &rhdr);
331 	else
332 		putwait(spc, &rw, &rhdr);
333 	rv = dosend(spc, &rhdr, sizeof(rhdr));
334 	if (auth) {
335 		memcpy(rf.rf_auth, auth, AUTHLEN*sizeof(*auth));
336 		rf.rf_cancel = cancel;
337 		rv = send_with_recon(spc, &rf, sizeof(rf));
338 	} else {
339 		rv = dosend(spc, getprogname(), strlen(getprogname())+1);
340 	}
341 	if (rv || cancel) {
342 		if (haslock)
343 			unputwait_locked(spc, &rw);
344 		else
345 			unputwait(spc, &rw);
346 		if (cancel) {
347 			pthread_sigmask(SIG_SETMASK, &omask, NULL);
348 			return rv;
349 		}
350 	} else {
351 		rv = cliwaitresp(spc, &rw, &omask, haslock);
352 	}
353 	pthread_sigmask(SIG_SETMASK, &omask, NULL);
354 	if (rv)
355 		return rv;
356 
357 	rv = *(int *)rw.rw_data;
358 	free(rw.rw_data);
359 
360 	return rv;
361 }
362 
363 static int
364 prefork_req(struct spclient *spc, void **resp)
365 {
366 	struct rsp_hdr rhdr;
367 	struct respwait rw;
368 	sigset_t omask;
369 	int rv;
370 
371 	rhdr.rsp_len = sizeof(rhdr);
372 	rhdr.rsp_class = RUMPSP_REQ;
373 	rhdr.rsp_type = RUMPSP_PREFORK;
374 	rhdr.rsp_error = 0;
375 
376 	pthread_sigmask(SIG_SETMASK, &fullset, &omask);
377 	do {
378 		putwait(spc, &rw, &rhdr);
379 		rv = send_with_recon(spc, &rhdr, sizeof(rhdr));
380 		if (rv != 0) {
381 			unputwait(spc, &rw);
382 			continue;
383 		}
384 
385 		rv = cliwaitresp(spc, &rw, &omask, false);
386 		if (rv == ENOTCONN)
387 			rv = EAGAIN;
388 	} while (rv == EAGAIN);
389 	pthread_sigmask(SIG_SETMASK, &omask, NULL);
390 
391 	*resp = rw.rw_data;
392 	return rv;
393 }
394 
395 /*
396  * prevent response code from deadlocking with reconnect code
397  */
398 static int
399 resp_sendlock(struct spclient *spc)
400 {
401 	int rv = 0;
402 
403 	pthread_mutex_lock(&spc->spc_mtx);
404 	while (spc->spc_ostatus != SPCSTATUS_FREE) {
405 		if (__predict_false(spc->spc_reconnecting)) {
406 			rv = EBUSY;
407 			goto out;
408 		}
409 		spc->spc_ostatus = SPCSTATUS_WANTED;
410 		pthread_cond_wait(&spc->spc_cv, &spc->spc_mtx);
411 	}
412 	spc->spc_ostatus = SPCSTATUS_BUSY;
413 
414  out:
415 	pthread_mutex_unlock(&spc->spc_mtx);
416 	return rv;
417 }
418 
419 static void
420 send_copyin_resp(struct spclient *spc, uint64_t reqno, void *data, size_t dlen,
421 	int wantstr)
422 {
423 	struct rsp_hdr rhdr;
424 
425 	if (wantstr)
426 		dlen = MIN(dlen, strlen(data)+1);
427 
428 	rhdr.rsp_len = sizeof(rhdr) + dlen;
429 	rhdr.rsp_reqno = reqno;
430 	rhdr.rsp_class = RUMPSP_RESP;
431 	rhdr.rsp_type = RUMPSP_COPYIN;
432 	rhdr.rsp_sysnum = 0;
433 
434 	if (resp_sendlock(spc) != 0)
435 		return;
436 	(void)dosend(spc, &rhdr, sizeof(rhdr));
437 	(void)dosend(spc, data, dlen);
438 	sendunlock(spc);
439 }
440 
441 static void
442 send_anonmmap_resp(struct spclient *spc, uint64_t reqno, void *addr)
443 {
444 	struct rsp_hdr rhdr;
445 
446 	rhdr.rsp_len = sizeof(rhdr) + sizeof(addr);
447 	rhdr.rsp_reqno = reqno;
448 	rhdr.rsp_class = RUMPSP_RESP;
449 	rhdr.rsp_type = RUMPSP_ANONMMAP;
450 	rhdr.rsp_sysnum = 0;
451 
452 	if (resp_sendlock(spc) != 0)
453 		return;
454 	(void)dosend(spc, &rhdr, sizeof(rhdr));
455 	(void)dosend(spc, &addr, sizeof(addr));
456 	sendunlock(spc);
457 }
458 
459 int
460 rumpclient_syscall(int sysnum, const void *data, size_t dlen,
461 	register_t *retval)
462 {
463 	struct rsp_sysresp *resp;
464 	void *rdata;
465 	int rv;
466 
467 	DPRINTF(("rumpsp syscall_req: syscall %d with %p/%zu\n",
468 	    sysnum, data, dlen));
469 
470 	rv = syscall_req(&clispc, sysnum, data, dlen, &rdata);
471 	if (rv)
472 		return rv;
473 
474 	resp = rdata;
475 	DPRINTF(("rumpsp syscall_resp: syscall %d error %d, rv: %d/%d\n",
476 	    sysnum, rv, resp->rsys_retval[0], resp->rsys_retval[1]));
477 
478 	memcpy(retval, &resp->rsys_retval, sizeof(resp->rsys_retval));
479 	rv = resp->rsys_error;
480 	free(rdata);
481 
482 	return rv;
483 }
484 
485 static void
486 handlereq(struct spclient *spc)
487 {
488 	struct rsp_copydata *copydata;
489 	struct rsp_hdr *rhdr = &spc->spc_hdr;
490 	void *mapaddr;
491 	size_t maplen;
492 	int reqtype = spc->spc_hdr.rsp_type;
493 
494 	switch (reqtype) {
495 	case RUMPSP_COPYIN:
496 	case RUMPSP_COPYINSTR:
497 		/*LINTED*/
498 		copydata = (struct rsp_copydata *)spc->spc_buf;
499 		DPRINTF(("rump_sp handlereq: copyin request: %p/%zu\n",
500 		    copydata->rcp_addr, copydata->rcp_len));
501 		send_copyin_resp(spc, spc->spc_hdr.rsp_reqno,
502 		    copydata->rcp_addr, copydata->rcp_len,
503 		    reqtype == RUMPSP_COPYINSTR);
504 		break;
505 	case RUMPSP_COPYOUT:
506 	case RUMPSP_COPYOUTSTR:
507 		/*LINTED*/
508 		copydata = (struct rsp_copydata *)spc->spc_buf;
509 		DPRINTF(("rump_sp handlereq: copyout request: %p/%zu\n",
510 		    copydata->rcp_addr, copydata->rcp_len));
511 		/*LINTED*/
512 		memcpy(copydata->rcp_addr, copydata->rcp_data,
513 		    copydata->rcp_len);
514 		break;
515 	case RUMPSP_ANONMMAP:
516 		/*LINTED*/
517 		maplen = *(size_t *)spc->spc_buf;
518 		mapaddr = mmap(NULL, maplen, PROT_READ|PROT_WRITE,
519 		    MAP_ANON, -1, 0);
520 		if (mapaddr == MAP_FAILED)
521 			mapaddr = NULL;
522 		DPRINTF(("rump_sp handlereq: anonmmap: %p\n", mapaddr));
523 		send_anonmmap_resp(spc, spc->spc_hdr.rsp_reqno, mapaddr);
524 		break;
525 	case RUMPSP_RAISE:
526 		DPRINTF(("rump_sp handlereq: raise sig %d\n", rhdr->rsp_signo));
527 		raise((int)rhdr->rsp_signo);
528 		/*
529 		 * We most likely have signals blocked, but the signal
530 		 * will be handled soon enough when we return.
531 		 */
532 		break;
533 	default:
534 		printf("PANIC: INVALID TYPE %d\n", reqtype);
535 		abort();
536 		break;
537 	}
538 
539 	spcfreebuf(spc);
540 }
541 
542 static unsigned ptab_idx;
543 static struct sockaddr *serv_sa;
544 
545 static int
546 doconnect(bool noisy)
547 {
548 	struct respwait rw;
549 	struct rsp_hdr rhdr;
550 	struct kevent kev[NSIG+1];
551 	char banner[MAXBANNER];
552 	struct pollfd pfd;
553 	int s, error, flags, i;
554 	ssize_t n;
555 
556 	if (kq != -1)
557 		host_close(kq);
558 	kq = -1;
559 	s = -1;
560 
561 	if (clispc.spc_fd != -1)
562 		host_close(clispc.spc_fd);
563 	clispc.spc_fd = -1;
564 
565 	/*
566 	 * for reconnect, gate everyone out of the receiver code
567 	 */
568 	putwait_locked(&clispc, &rw, &rhdr);
569 
570 	pthread_mutex_lock(&clispc.spc_mtx);
571 	clispc.spc_reconnecting = 1;
572 	pthread_cond_broadcast(&clispc.spc_cv);
573 	clispc.spc_generation++;
574 	while (clispc.spc_istatus != SPCSTATUS_FREE) {
575 		clispc.spc_istatus = SPCSTATUS_WANTED;
576 		pthread_cond_wait(&rw.rw_cv, &clispc.spc_mtx);
577 	}
578 	kickall(&clispc);
579 
580 	/*
581 	 * we can release it already since we hold the
582 	 * send lock during reconnect
583 	 * XXX: assert it
584 	 */
585 	clispc.spc_istatus = SPCSTATUS_FREE;
586 	pthread_mutex_unlock(&clispc.spc_mtx);
587 	unputwait_locked(&clispc, &rw);
588 
589 	free(clispc.spc_buf);
590 	clispc.spc_off = 0;
591 
592 	s = host_socket(parsetab[ptab_idx].domain, SOCK_STREAM, 0);
593 	if (s == -1)
594 		return -1;
595 
596 	pfd.fd = s;
597 	pfd.events = POLLIN;
598 	while (host_connect(s, serv_sa, (socklen_t)serv_sa->sa_len) == -1) {
599 		if (errno == EINTR)
600 			continue;
601 		error = errno;
602 		if (noisy)
603 			fprintf(stderr, "rump_sp: client connect failed: %s\n",
604 			    strerror(errno));
605 		errno = error;
606 		return -1;
607 	}
608 
609 	if ((error = parsetab[ptab_idx].connhook(s)) != 0) {
610 		error = errno;
611 		if (noisy)
612 			fprintf(stderr, "rump_sp: connect hook failed\n");
613 		errno = error;
614 		return -1;
615 	}
616 
617 	if ((n = host_read(s, banner, sizeof(banner)-1)) < 0) {
618 		error = errno;
619 		if (noisy)
620 			fprintf(stderr, "rump_sp: failed to read banner\n");
621 		errno = error;
622 		return -1;
623 	}
624 
625 	if (banner[n-1] != '\n') {
626 		if (noisy)
627 			fprintf(stderr, "rump_sp: invalid banner\n");
628 		errno = EINVAL;
629 		return -1;
630 	}
631 	banner[n] = '\0';
632 	/* parse the banner some day */
633 
634 	flags = host_fcntl(s, F_GETFL, 0);
635 	if (host_fcntl(s, F_SETFL, flags | O_NONBLOCK) == -1) {
636 		if (noisy)
637 			fprintf(stderr, "rump_sp: socket fd NONBLOCK: %s\n",
638 			    strerror(errno));
639 		errno = EINVAL;
640 		return -1;
641 	}
642 	clispc.spc_fd = s;
643 	clispc.spc_state = SPCSTATE_RUNNING;
644 	clispc.spc_reconnecting = 0;
645 
646 	/* setup kqueue, we want all signals and the fd */
647 	if ((kq = host_kqueue()) == -1) {
648 		error = errno;
649 		if (noisy)
650 			fprintf(stderr, "rump_sp: cannot setup kqueue");
651 		errno = error;
652 		return -1;
653 	}
654 
655 	for (i = 0; i < NSIG; i++) {
656 		EV_SET(&kev[i], i+1, EVFILT_SIGNAL, EV_ADD|EV_ENABLE, 0, 0, 0);
657 	}
658 	EV_SET(&kev[NSIG], clispc.spc_fd,
659 	    EVFILT_READ, EV_ADD|EV_ENABLE, 0, 0, 0);
660 	if (host_kevent(kq, kev, NSIG+1, NULL, 0, NULL) == -1) {
661 		error = errno;
662 		if (noisy)
663 			fprintf(stderr, "rump_sp: kevent() failed");
664 		errno = error;
665 		return -1;
666 	}
667 
668 	return 0;
669 }
670 
671 static int
672 doinit(void)
673 {
674 
675 	TAILQ_INIT(&clispc.spc_respwait);
676 	pthread_mutex_init(&clispc.spc_mtx, NULL);
677 	pthread_cond_init(&clispc.spc_cv, NULL);
678 
679 	return 0;
680 }
681 
682 void *(*rumpclient_dlsym)(void *, const char *);
683 
684 int
685 rumpclient_init()
686 {
687 	char *p;
688 	int error;
689 
690 	/* dlsym overrided by rumphijack? */
691 	if (!rumpclient_dlsym)
692 		rumpclient_dlsym = dlsym;
693 
694 	/*
695 	 * sag mir, wo die symbol sind.  zogen fort, der krieg beginnt.
696 	 * wann wird man je verstehen?  wann wird man je verstehen?
697 	 */
698 #define FINDSYM2(_name_,_syscall_)					\
699 	if ((host_##_name_ = rumpclient_dlsym(RTLD_NEXT,		\
700 	    #_syscall_)) == NULL)					\
701 		/* host_##_name_ = _syscall_ */;
702 #define FINDSYM(_name_) FINDSYM2(_name_,_name_)
703 	FINDSYM2(socket,__socket30);
704 	FINDSYM(close);
705 	FINDSYM(connect);
706 	FINDSYM(fcntl);
707 	FINDSYM(poll);
708 	FINDSYM(read);
709 	FINDSYM(sendto);
710 	FINDSYM(setsockopt);
711 	FINDSYM(kqueue);
712 #if !__NetBSD_Prereq__(5,99,7)
713 	FINDSYM(kevent);
714 #else
715 	FINDSYM2(kevent,_sys___kevent50);
716 #endif
717 #undef	FINDSYM
718 #undef	FINDSY2
719 
720 	if ((p = getenv("RUMP_SERVER")) == NULL) {
721 		errno = ENOENT;
722 		return -1;
723 	}
724 
725 	if ((error = parseurl(p, &serv_sa, &ptab_idx, 0)) != 0) {
726 		errno = error;
727 		return -1;
728 	}
729 
730 	if (doinit() == -1)
731 		return -1;
732 	if (doconnect(true) == -1)
733 		return -1;
734 
735 	error = handshake_req(&clispc, NULL, 0, false);
736 	if (error) {
737 		pthread_mutex_destroy(&clispc.spc_mtx);
738 		pthread_cond_destroy(&clispc.spc_cv);
739 		if (clispc.spc_fd != -1)
740 			host_close(clispc.spc_fd);
741 		errno = error;
742 		return -1;
743 	}
744 
745 	sigfillset(&fullset);
746 	return 0;
747 }
748 
749 struct rumpclient_fork {
750 	uint32_t fork_auth[AUTHLEN];
751 };
752 
753 struct rumpclient_fork *
754 rumpclient_prefork(void)
755 {
756 	struct rumpclient_fork *rpf;
757 	void *resp;
758 	int rv;
759 
760 	rpf = malloc(sizeof(*rpf));
761 	if (rpf == NULL)
762 		return NULL;
763 
764 	if ((rv = prefork_req(&clispc, &resp)) != 0) {
765 		free(rpf);
766 		errno = rv;
767 		return NULL;
768 	}
769 
770 	memcpy(rpf->fork_auth, resp, sizeof(rpf->fork_auth));
771 	free(resp);
772 
773 	return rpf;
774 }
775 
776 int
777 rumpclient_fork_init(struct rumpclient_fork *rpf)
778 {
779 	int error;
780 
781 	memset(&clispc, 0, sizeof(clispc));
782 	clispc.spc_fd = -1;
783 	kq = -1;
784 
785 	if (doinit() == -1)
786 		return -1;
787 	if (doconnect(false) == -1)
788 		return -1;
789 
790 	error = handshake_req(&clispc, rpf->fork_auth, 0, false);
791 	if (error) {
792 		pthread_mutex_destroy(&clispc.spc_mtx);
793 		pthread_cond_destroy(&clispc.spc_cv);
794 		errno = error;
795 		return -1;
796 	}
797 
798 	return 0;
799 }
800 
801 void
802 rumpclient_setconnretry(time_t timeout)
803 {
804 
805 	if (timeout < RUMPCLIENT_RETRYCONN_ONCE)
806 		return; /* gigo */
807 
808 	retrytimo = timeout;
809 }
810