Home | History | Annotate | Line # | Download | only in librumpclient
rumpclient.c revision 1.12
      1 /*      $NetBSD: rumpclient.c,v 1.12 2011/01/06 06:57:14 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/mman.h>
     37 #include <sys/socket.h>
     38 
     39 #include <arpa/inet.h>
     40 #include <netinet/in.h>
     41 #include <netinet/tcp.h>
     42 
     43 #include <assert.h>
     44 #include <errno.h>
     45 #include <fcntl.h>
     46 #include <poll.h>
     47 #include <pthread.h>
     48 #include <signal.h>
     49 #include <stdarg.h>
     50 #include <stdio.h>
     51 #include <stdlib.h>
     52 #include <string.h>
     53 #include <unistd.h>
     54 
     55 #include <rump/rumpclient.h>
     56 
     57 #include "sp_common.c"
     58 
     59 static struct spclient clispc = {
     60 	.spc_fd = -1,
     61 };
     62 
     63 /*
     64  * This version of waitresp is optimized for single-threaded clients
     65  * and is required by signal-safe clientside rump syscalls.
     66  */
     67 
     68 static void
     69 releasercvlock(struct spclient *spc)
     70 {
     71 
     72 	pthread_mutex_lock(&spc->spc_mtx);
     73 	if (spc->spc_istatus == SPCSTATUS_WANTED)
     74 		kickall(spc);
     75 	spc->spc_istatus = SPCSTATUS_FREE;
     76 }
     77 
     78 static sigset_t fullset;
     79 static int
     80 waitresp(struct spclient *spc, struct respwait *rw, sigset_t *mask)
     81 {
     82 	struct pollfd pfd;
     83 	int rv = 0;
     84 
     85 	sendunlockl(spc);
     86 
     87 	rw->rw_error = 0;
     88 	while (rw->rw_data == NULL && rw->rw_error == 0
     89 	    && spc->spc_state != SPCSTATE_DYING){
     90 		/* are we free to receive? */
     91 		if (spc->spc_istatus == SPCSTATUS_FREE) {
     92 			spc->spc_istatus = SPCSTATUS_BUSY;
     93 			pthread_mutex_unlock(&spc->spc_mtx);
     94 
     95 			pfd.fd = spc->spc_fd;
     96 			pfd.events = POLLIN;
     97 
     98 			switch (readframe(spc)) {
     99 			case 0:
    100 				releasercvlock(spc);
    101 				pthread_mutex_unlock(&spc->spc_mtx);
    102 				pollts(&pfd, 1, NULL, mask);
    103 				pthread_mutex_lock(&spc->spc_mtx);
    104 				continue;
    105 			case -1:
    106 				releasercvlock(spc);
    107 				rv = errno;
    108 				spc->spc_state = SPCSTATE_DYING;
    109 				continue;
    110 			default:
    111 				break;
    112 			}
    113 
    114 			switch (spc->spc_hdr.rsp_class) {
    115 				case RUMPSP_RESP:
    116 				case RUMPSP_ERROR:
    117 					kickwaiter(spc);
    118 					break;
    119 				case RUMPSP_REQ:
    120 					handlereq(spc);
    121 					break;
    122 				default:
    123 					/* panic */
    124 					break;
    125 			}
    126 
    127 			releasercvlock(spc);
    128 		} else {
    129 			spc->spc_istatus = SPCSTATUS_WANTED;
    130 			pthread_cond_wait(&rw->rw_cv, &spc->spc_mtx);
    131 		}
    132 	}
    133 	TAILQ_REMOVE(&spc->spc_respwait, rw, rw_entries);
    134 	pthread_mutex_unlock(&spc->spc_mtx);
    135 	pthread_cond_destroy(&rw->rw_cv);
    136 
    137 	if (rv)
    138 		return rv;
    139 	if (spc->spc_state == SPCSTATE_DYING)
    140 		return ENOTCONN;
    141 	return rw->rw_error;
    142 }
    143 
    144 
    145 static int
    146 syscall_req(struct spclient *spc, int sysnum,
    147 	const void *data, size_t dlen, void **resp)
    148 {
    149 	struct rsp_hdr rhdr;
    150 	struct respwait rw;
    151 	sigset_t omask;
    152 	int rv;
    153 
    154 	rhdr.rsp_len = sizeof(rhdr) + dlen;
    155 	rhdr.rsp_class = RUMPSP_REQ;
    156 	rhdr.rsp_type = RUMPSP_SYSCALL;
    157 	rhdr.rsp_sysnum = sysnum;
    158 
    159 	pthread_sigmask(SIG_SETMASK, &fullset, &omask);
    160 	do {
    161 
    162 		putwait(spc, &rw, &rhdr);
    163 		rv = dosend(spc, &rhdr, sizeof(rhdr));
    164 		rv = dosend(spc, data, dlen);
    165 		if (rv) {
    166 			unputwait(spc, &rw);
    167 			pthread_sigmask(SIG_SETMASK, &omask, NULL);
    168 			return rv;
    169 		}
    170 
    171 		rv = waitresp(spc, &rw, &omask);
    172 	} while (rv == EAGAIN);
    173 	pthread_sigmask(SIG_SETMASK, &omask, NULL);
    174 
    175 	*resp = rw.rw_data;
    176 	return rv;
    177 }
    178 
    179 static int
    180 handshake_req(struct spclient *spc, uint32_t *auth, int cancel)
    181 {
    182 	struct handshake_fork rf;
    183 	struct rsp_hdr rhdr;
    184 	struct respwait rw;
    185 	sigset_t omask;
    186 	int rv;
    187 
    188 	/* performs server handshake */
    189 	rhdr.rsp_len = sizeof(rhdr) + (auth ? sizeof(rf) : 0);
    190 	rhdr.rsp_class = RUMPSP_REQ;
    191 	rhdr.rsp_type = RUMPSP_HANDSHAKE;
    192 	if (auth)
    193 		rhdr.rsp_handshake = HANDSHAKE_FORK;
    194 	else
    195 		rhdr.rsp_handshake = HANDSHAKE_GUEST;
    196 
    197 	pthread_sigmask(SIG_SETMASK, &fullset, &omask);
    198 	putwait(spc, &rw, &rhdr);
    199 	rv = dosend(spc, &rhdr, sizeof(rhdr));
    200 	if (auth) {
    201 		memcpy(rf.rf_auth, auth, AUTHLEN*sizeof(*auth));
    202 		rf.rf_cancel = cancel;
    203 		rv = dosend(spc, &rf, sizeof(rf));
    204 	}
    205 	if (rv != 0 || cancel) {
    206 		unputwait(spc, &rw);
    207 		pthread_sigmask(SIG_SETMASK, &omask, NULL);
    208 		return rv;
    209 	}
    210 
    211 	rv = waitresp(spc, &rw, &omask);
    212 	pthread_sigmask(SIG_SETMASK, &omask, NULL);
    213 	if (rv)
    214 		return rv;
    215 
    216 	rv = *(int *)rw.rw_data;
    217 	free(rw.rw_data);
    218 
    219 	return rv;
    220 }
    221 
    222 static int
    223 prefork_req(struct spclient *spc, void **resp)
    224 {
    225 	struct rsp_hdr rhdr;
    226 	struct respwait rw;
    227 	sigset_t omask;
    228 	int rv;
    229 
    230 	rhdr.rsp_len = sizeof(rhdr);
    231 	rhdr.rsp_class = RUMPSP_REQ;
    232 	rhdr.rsp_type = RUMPSP_PREFORK;
    233 	rhdr.rsp_error = 0;
    234 
    235 	pthread_sigmask(SIG_SETMASK, &fullset, &omask);
    236 	putwait(spc, &rw, &rhdr);
    237 	rv = dosend(spc, &rhdr, sizeof(rhdr));
    238 	if (rv != 0) {
    239 		unputwait(spc, &rw);
    240 		pthread_sigmask(SIG_SETMASK, &omask, NULL);
    241 		return rv;
    242 	}
    243 
    244 	rv = waitresp(spc, &rw, &omask);
    245 	pthread_sigmask(SIG_SETMASK, &omask, NULL);
    246 	*resp = rw.rw_data;
    247 	return rv;
    248 }
    249 
    250 static int
    251 send_copyin_resp(struct spclient *spc, uint64_t reqno, void *data, size_t dlen,
    252 	int wantstr)
    253 {
    254 	struct rsp_hdr rhdr;
    255 	int rv;
    256 
    257 	if (wantstr)
    258 		dlen = MIN(dlen, strlen(data)+1);
    259 
    260 	rhdr.rsp_len = sizeof(rhdr) + dlen;
    261 	rhdr.rsp_reqno = reqno;
    262 	rhdr.rsp_class = RUMPSP_RESP;
    263 	rhdr.rsp_type = RUMPSP_COPYIN;
    264 	rhdr.rsp_sysnum = 0;
    265 
    266 	sendlock(spc);
    267 	rv = dosend(spc, &rhdr, sizeof(rhdr));
    268 	rv = dosend(spc, data, dlen);
    269 	sendunlock(spc);
    270 
    271 	return rv;
    272 }
    273 
    274 static int
    275 send_anonmmap_resp(struct spclient *spc, uint64_t reqno, void *addr)
    276 {
    277 	struct rsp_hdr rhdr;
    278 	int rv;
    279 
    280 	rhdr.rsp_len = sizeof(rhdr) + sizeof(addr);
    281 	rhdr.rsp_reqno = reqno;
    282 	rhdr.rsp_class = RUMPSP_RESP;
    283 	rhdr.rsp_type = RUMPSP_ANONMMAP;
    284 	rhdr.rsp_sysnum = 0;
    285 
    286 	sendlock(spc);
    287 	rv = dosend(spc, &rhdr, sizeof(rhdr));
    288 	rv = dosend(spc, &addr, sizeof(addr));
    289 	sendunlock(spc);
    290 
    291 	return rv;
    292 }
    293 
    294 int
    295 rumpclient_syscall(int sysnum, const void *data, size_t dlen,
    296 	register_t *retval)
    297 {
    298 	struct rsp_sysresp *resp;
    299 	void *rdata;
    300 	int rv;
    301 
    302 	DPRINTF(("rumpsp syscall_req: syscall %d with %p/%zu\n",
    303 	    sysnum, data, dlen));
    304 
    305 	rv = syscall_req(&clispc, sysnum, data, dlen, &rdata);
    306 	if (rv)
    307 		return rv;
    308 
    309 	resp = rdata;
    310 	DPRINTF(("rumpsp syscall_resp: syscall %d error %d, rv: %d/%d\n",
    311 	    sysnum, rv, resp->rsys_retval[0], resp->rsys_retval[1]));
    312 
    313 	memcpy(retval, &resp->rsys_retval, sizeof(resp->rsys_retval));
    314 	rv = resp->rsys_error;
    315 	free(rdata);
    316 
    317 	return rv;
    318 }
    319 
    320 static void
    321 handlereq(struct spclient *spc)
    322 {
    323 	struct rsp_copydata *copydata;
    324 	void *mapaddr;
    325 	size_t maplen;
    326 	int reqtype = spc->spc_hdr.rsp_type;
    327 
    328 	switch (reqtype) {
    329 	case RUMPSP_COPYIN:
    330 	case RUMPSP_COPYINSTR:
    331 		/*LINTED*/
    332 		copydata = (struct rsp_copydata *)spc->spc_buf;
    333 		DPRINTF(("rump_sp handlereq: copyin request: %p/%zu\n",
    334 		    copydata->rcp_addr, copydata->rcp_len));
    335 		send_copyin_resp(spc, spc->spc_hdr.rsp_reqno,
    336 		    copydata->rcp_addr, copydata->rcp_len,
    337 		    reqtype == RUMPSP_COPYINSTR);
    338 		break;
    339 	case RUMPSP_COPYOUT:
    340 	case RUMPSP_COPYOUTSTR:
    341 		/*LINTED*/
    342 		copydata = (struct rsp_copydata *)spc->spc_buf;
    343 		DPRINTF(("rump_sp handlereq: copyout request: %p/%zu\n",
    344 		    copydata->rcp_addr, copydata->rcp_len));
    345 		/*LINTED*/
    346 		memcpy(copydata->rcp_addr, copydata->rcp_data,
    347 		    copydata->rcp_len);
    348 		break;
    349 	case RUMPSP_ANONMMAP:
    350 		/*LINTED*/
    351 		maplen = *(size_t *)spc->spc_buf;
    352 		mapaddr = mmap(NULL, maplen, PROT_READ|PROT_WRITE,
    353 		    MAP_ANON, -1, 0);
    354 		if (mapaddr == MAP_FAILED)
    355 			mapaddr = NULL;
    356 		DPRINTF(("rump_sp handlereq: anonmmap: %p\n", mapaddr));
    357 		send_anonmmap_resp(spc, spc->spc_hdr.rsp_reqno, mapaddr);
    358 		break;
    359 	default:
    360 		printf("PANIC: INVALID TYPE %d\n", reqtype);
    361 		abort();
    362 		break;
    363 	}
    364 
    365 	spcfreebuf(spc);
    366 }
    367 
    368 static unsigned ptab_idx;
    369 static struct sockaddr *serv_sa;
    370 
    371 static int
    372 doconnect(void)
    373 {
    374 	char banner[MAXBANNER];
    375 	int s, error;
    376 	ssize_t n;
    377 
    378 	s = socket(parsetab[ptab_idx].domain, SOCK_STREAM, 0);
    379 	if (s == -1)
    380 		return -1;
    381 
    382 	if (connect(s, serv_sa, (socklen_t)serv_sa->sa_len) == -1) {
    383 		error = errno;
    384 		fprintf(stderr, "rump_sp: client connect failed\n");
    385 		errno = error;
    386 		return -1;
    387 	}
    388 
    389 	if ((error = parsetab[ptab_idx].connhook(s)) != 0) {
    390 		error = errno;
    391 		fprintf(stderr, "rump_sp: connect hook failed\n");
    392 		errno = error;
    393 		return -1;
    394 	}
    395 
    396 	if ((n = read(s, banner, sizeof(banner)-1)) < 0) {
    397 		error = errno;
    398 		fprintf(stderr, "rump_sp: failed to read banner\n");
    399 		errno = error;
    400 		return -1;
    401 	}
    402 
    403 	if (banner[n-1] != '\n') {
    404 		fprintf(stderr, "rump_sp: invalid banner\n");
    405 		errno = EINVAL;
    406 		return -1;
    407 	}
    408 	banner[n] = '\0';
    409 
    410 	/* parse the banner some day */
    411 
    412 	clispc.spc_fd = s;
    413 	TAILQ_INIT(&clispc.spc_respwait);
    414 	pthread_mutex_init(&clispc.spc_mtx, NULL);
    415 	pthread_cond_init(&clispc.spc_cv, NULL);
    416 
    417 	return 0;
    418 }
    419 
    420 int
    421 rumpclient_init()
    422 {
    423 	char *p;
    424 	int error;
    425 
    426 	if ((p = getenv("RUMP_SERVER")) == NULL) {
    427 		errno = ENOENT;
    428 		return -1;
    429 	}
    430 
    431 	if ((error = parseurl(p, &serv_sa, &ptab_idx, 0)) != 0) {
    432 		errno = error;
    433 		return -1;
    434 	}
    435 
    436 	if (doconnect() == -1)
    437 		return -1;
    438 
    439 	error = handshake_req(&clispc, NULL, 0);
    440 	if (error) {
    441 		pthread_mutex_destroy(&clispc.spc_mtx);
    442 		pthread_cond_destroy(&clispc.spc_cv);
    443 		close(clispc.spc_fd);
    444 		errno = error;
    445 		return -1;
    446 	}
    447 
    448 	sigfillset(&fullset);
    449 	return 0;
    450 }
    451 
    452 struct rumpclient_fork {
    453 	uint32_t fork_auth[AUTHLEN];
    454 };
    455 
    456 struct rumpclient_fork *
    457 rumpclient_prefork(void)
    458 {
    459 	struct rumpclient_fork *rpf;
    460 	void *resp;
    461 	int rv;
    462 
    463 	rpf = malloc(sizeof(*rpf));
    464 	if (rpf == NULL)
    465 		return NULL;
    466 
    467 	if ((rv = prefork_req(&clispc, &resp)) != 0) {
    468 		free(rpf);
    469 		errno = rv;
    470 		return NULL;
    471 	}
    472 
    473 	memcpy(rpf->fork_auth, resp, sizeof(rpf->fork_auth));
    474 	free(resp);
    475 
    476 	return rpf;
    477 }
    478 
    479 int
    480 rumpclient_fork_init(struct rumpclient_fork *rpf)
    481 {
    482 	int error;
    483 
    484 	close(clispc.spc_fd);
    485 	memset(&clispc, 0, sizeof(clispc));
    486 	clispc.spc_fd = -1;
    487 
    488 	if (doconnect() == -1)
    489 		return -1;
    490 
    491 	error = handshake_req(&clispc, rpf->fork_auth, 0);
    492 	if (error) {
    493 		pthread_mutex_destroy(&clispc.spc_mtx);
    494 		pthread_cond_destroy(&clispc.spc_cv);
    495 		errno = error;
    496 		return -1;
    497 	}
    498 
    499 	return 0;
    500 }
    501