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