xref: /netbsd-src/lib/librumpclient/rumpclient.c (revision 4817a0b0b8fe9612e8ebe21a9bf2d97b95038a97)
1 /*      $NetBSD: rumpclient.c,v 1.10 2010/12/16 17:05:44 pooka Exp $	*/
2 
3 /*
4  * Copyright (c) 2010 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 <stdarg.h>
49 #include <stdio.h>
50 #include <stdlib.h>
51 #include <string.h>
52 #include <unistd.h>
53 
54 #include <rump/rumpclient.h>
55 
56 #include "sp_common.c"
57 
58 static struct spclient clispc;
59 
60 static int
61 syscall_req(struct spclient *spc, int sysnum,
62 	const void *data, size_t dlen, void **resp)
63 {
64 	struct rsp_hdr rhdr;
65 	struct respwait rw;
66 	int rv;
67 
68 	rhdr.rsp_len = sizeof(rhdr) + dlen;
69 	rhdr.rsp_class = RUMPSP_REQ;
70 	rhdr.rsp_type = RUMPSP_SYSCALL;
71 	rhdr.rsp_sysnum = sysnum;
72 
73 	do {
74 		putwait(spc, &rw, &rhdr);
75 		rv = dosend(spc, &rhdr, sizeof(rhdr));
76 		rv = dosend(spc, data, dlen);
77 		if (rv) {
78 			unputwait(spc, &rw);
79 			return rv;
80 		}
81 
82 		rv = waitresp(spc, &rw);
83 	} while (rv == EAGAIN);
84 
85 	*resp = rw.rw_data;
86 	return rv;
87 }
88 
89 static int
90 handshake_req(struct spclient *spc)
91 {
92 	struct rsp_hdr rhdr;
93 	struct respwait rw;
94 	int rv;
95 
96 	/* performs server handshake */
97 	rhdr.rsp_len = sizeof(rhdr);
98 	rhdr.rsp_class = RUMPSP_REQ;
99 	rhdr.rsp_type = RUMPSP_HANDSHAKE;
100 	rhdr.rsp_handshake = HANDSHAKE_GUEST;
101 
102 	putwait(spc, &rw, &rhdr);
103 	rv = dosend(spc, &rhdr, sizeof(rhdr));
104 	if (rv != 0) {
105 		unputwait(spc, &rw);
106 		return rv;
107 	}
108 
109 	rv = waitresp(spc, &rw);
110 	if (rv)
111 		return rv;
112 
113 	rv = *(int *)rw.rw_data;
114 	free(rw.rw_data);
115 
116 	return rv;
117 }
118 
119 static int
120 send_copyin_resp(struct spclient *spc, uint64_t reqno, void *data, size_t dlen,
121 	int wantstr)
122 {
123 	struct rsp_hdr rhdr;
124 	int rv;
125 
126 	if (wantstr)
127 		dlen = MIN(dlen, strlen(data)+1);
128 
129 	rhdr.rsp_len = sizeof(rhdr) + dlen;
130 	rhdr.rsp_reqno = reqno;
131 	rhdr.rsp_class = RUMPSP_RESP;
132 	rhdr.rsp_type = RUMPSP_COPYIN;
133 	rhdr.rsp_sysnum = 0;
134 
135 	sendlock(spc);
136 	rv = dosend(spc, &rhdr, sizeof(rhdr));
137 	rv = dosend(spc, data, dlen);
138 	sendunlock(spc);
139 
140 	return rv;
141 }
142 
143 static int
144 send_anonmmap_resp(struct spclient *spc, uint64_t reqno, void *addr)
145 {
146 	struct rsp_hdr rhdr;
147 	int rv;
148 
149 	rhdr.rsp_len = sizeof(rhdr) + sizeof(addr);
150 	rhdr.rsp_reqno = reqno;
151 	rhdr.rsp_class = RUMPSP_RESP;
152 	rhdr.rsp_type = RUMPSP_ANONMMAP;
153 	rhdr.rsp_sysnum = 0;
154 
155 	sendlock(spc);
156 	rv = dosend(spc, &rhdr, sizeof(rhdr));
157 	rv = dosend(spc, &addr, sizeof(addr));
158 	sendunlock(spc);
159 
160 	return rv;
161 }
162 
163 int
164 rumpclient_syscall(int sysnum, const void *data, size_t dlen,
165 	register_t *retval)
166 {
167 	struct rsp_sysresp *resp;
168 	void *rdata;
169 	int rv;
170 
171 	DPRINTF(("rumpsp syscall_req: syscall %d with %p/%zu\n",
172 	    sysnum, data, dlen));
173 
174 	rv = syscall_req(&clispc, sysnum, data, dlen, &rdata);
175 	if (rv)
176 		return rv;
177 
178 	resp = rdata;
179 	DPRINTF(("rumpsp syscall_resp: syscall %d error %d, rv: %d/%d\n",
180 	    sysnum, rv, resp->rsys_retval[0], resp->rsys_retval[1]));
181 
182 	memcpy(retval, &resp->rsys_retval, sizeof(resp->rsys_retval));
183 	rv = resp->rsys_error;
184 	free(rdata);
185 
186 	return rv;
187 }
188 
189 static void
190 handlereq(struct spclient *spc)
191 {
192 	struct rsp_copydata *copydata;
193 	void *mapaddr;
194 	size_t maplen;
195 	int reqtype = spc->spc_hdr.rsp_type;
196 
197 	switch (reqtype) {
198 	case RUMPSP_COPYIN:
199 	case RUMPSP_COPYINSTR:
200 		/*LINTED*/
201 		copydata = (struct rsp_copydata *)spc->spc_buf;
202 		DPRINTF(("rump_sp handlereq: copyin request: %p/%zu\n",
203 		    copydata->rcp_addr, copydata->rcp_len));
204 		send_copyin_resp(spc, spc->spc_hdr.rsp_reqno,
205 		    copydata->rcp_addr, copydata->rcp_len,
206 		    reqtype == RUMPSP_COPYINSTR);
207 		break;
208 	case RUMPSP_COPYOUT:
209 	case RUMPSP_COPYOUTSTR:
210 		/*LINTED*/
211 		copydata = (struct rsp_copydata *)spc->spc_buf;
212 		DPRINTF(("rump_sp handlereq: copyout request: %p/%zu\n",
213 		    copydata->rcp_addr, copydata->rcp_len));
214 		/*LINTED*/
215 		memcpy(copydata->rcp_addr, copydata->rcp_data,
216 		    copydata->rcp_len);
217 		break;
218 	case RUMPSP_ANONMMAP:
219 		/*LINTED*/
220 		maplen = *(size_t *)spc->spc_buf;
221 		mapaddr = mmap(NULL, maplen, PROT_READ|PROT_WRITE,
222 		    MAP_ANON, -1, 0);
223 		if (mapaddr == MAP_FAILED)
224 			mapaddr = NULL;
225 		DPRINTF(("rump_sp handlereq: anonmmap: %p\n", mapaddr));
226 		send_anonmmap_resp(spc, spc->spc_hdr.rsp_reqno, mapaddr);
227 		break;
228 	default:
229 		printf("PANIC: INVALID TYPE\n");
230 		abort();
231 		break;
232 	}
233 
234 	spcfreebuf(spc);
235 }
236 
237 int
238 rumpclient_init()
239 {
240 	char banner[MAXBANNER];
241 	struct sockaddr *sap;
242 	char *p;
243 	unsigned idx;
244 	ssize_t n;
245 	int error, s;
246 
247 	if ((p = getenv("RUMP_SERVER")) == NULL) {
248 		errno = ENOENT;
249 		return -1;
250 	}
251 
252 	if ((error = parseurl(p, &sap, &idx, 0)) != 0) {
253 		errno = error;
254 		return -1;
255 	}
256 
257 	s = socket(parsetab[idx].domain, SOCK_STREAM, 0);
258 	if (s == -1)
259 		return -1;
260 
261 	if (connect(s, sap, (socklen_t)sap->sa_len) == -1) {
262 		error = errno;
263 		fprintf(stderr, "rump_sp: client connect failed\n");
264 		errno = error;
265 		return -1;
266 	}
267 
268 	if ((n = read(s, banner, sizeof(banner)-1)) < 0) {
269 		error = errno;
270 		fprintf(stderr, "rump_sp: failed to read banner\n");
271 		errno = error;
272 		return -1;
273 	}
274 
275 	if (banner[n-1] != '\n') {
276 		fprintf(stderr, "rump_sp: invalid banner\n");
277 		errno = EINVAL;
278 		return -1;
279 	}
280 	banner[n] = '\0';
281 
282 	/* parse the banner some day */
283 
284 	if ((error = parsetab[idx].connhook(s)) != 0) {
285 		error = errno;
286 		fprintf(stderr, "rump_sp: connect hook failed\n");
287 		errno = error;
288 		return -1;
289 	}
290 
291 	pthread_mutex_init(&clispc.spc_mtx, NULL);
292 	pthread_cond_init(&clispc.spc_cv, NULL);
293 	clispc.spc_fd = s;
294 	TAILQ_INIT(&clispc.spc_respwait);
295 
296 	error = handshake_req(&clispc);
297 	if (error) {
298 		pthread_mutex_destroy(&clispc.spc_mtx);
299 		pthread_cond_destroy(&clispc.spc_cv);
300 		close(s);
301 	}
302 	return error;
303 }
304