rumpclient.c revision 1.15 1 /* $NetBSD: rumpclient.c,v 1.15 2011/01/10 19:49:43 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 <stdio.h>
54 #include <stdlib.h>
55 #include <string.h>
56 #include <unistd.h>
57
58 #include <rump/rumpclient.h>
59
60 #define HOSTOPS
61 int (*host_socket)(int, int, int);
62 int (*host_close)(int);
63 int (*host_connect)(int, const struct sockaddr *, socklen_t);
64 int (*host_fcntl)(int, int, ...);
65 int (*host_poll)(struct pollfd *, nfds_t, int);
66 int (*host_pollts)(struct pollfd *, nfds_t, const struct timespec *,
67 const sigset_t *);
68 ssize_t (*host_read)(int, void *, size_t);
69 ssize_t (*host_sendto)(int, const void *, size_t, int,
70 const struct sockaddr *, socklen_t);
71 int (*host_setsockopt)(int, int, int, const void *, socklen_t);
72
73 #include "sp_common.c"
74
75 static struct spclient clispc = {
76 .spc_fd = -1,
77 };
78
79 static int kq;
80 static sigset_t fullset;
81
82 static int
83 waitresp(struct spclient *spc, struct respwait *rw, sigset_t *mask)
84 {
85
86 pthread_mutex_lock(&spc->spc_mtx);
87 sendunlockl(spc);
88
89 rw->rw_error = 0;
90 while (!rw->rw_done && rw->rw_error == 0
91 && spc->spc_state != SPCSTATE_DYING){
92 /* are we free to receive? */
93 if (spc->spc_istatus == SPCSTATUS_FREE) {
94 struct kevent kev[8];
95 int gotresp, dosig, rv, i;
96
97 spc->spc_istatus = SPCSTATUS_BUSY;
98 pthread_mutex_unlock(&spc->spc_mtx);
99
100 dosig = 0;
101 for (gotresp = 0; !gotresp; ) {
102 switch (readframe(spc)) {
103 case 0:
104 rv = kevent(kq, NULL, 0,
105 kev, __arraycount(kev), NULL);
106 assert(rv > 0);
107 for (i = 0; i < rv; i++) {
108 if (kev[i].filter
109 == EVFILT_SIGNAL)
110 dosig++;
111 }
112 if (dosig)
113 goto cleanup;
114
115 continue;
116 case -1:
117 spc->spc_state = SPCSTATE_DYING;
118 goto cleanup;
119 default:
120 break;
121 }
122
123 switch (spc->spc_hdr.rsp_class) {
124 case RUMPSP_RESP:
125 case RUMPSP_ERROR:
126 kickwaiter(spc);
127 gotresp = spc->spc_hdr.rsp_reqno ==
128 rw->rw_reqno;
129 break;
130 case RUMPSP_REQ:
131 handlereq(spc);
132 break;
133 default:
134 /* panic */
135 break;
136 }
137 }
138
139 cleanup:
140 pthread_mutex_lock(&spc->spc_mtx);
141 if (spc->spc_istatus == SPCSTATUS_WANTED)
142 kickall(spc);
143 spc->spc_istatus = SPCSTATUS_FREE;
144
145 /* take one for the team */
146 if (dosig) {
147 pthread_mutex_unlock(&spc->spc_mtx);
148 pthread_sigmask(SIG_SETMASK, mask, NULL);
149 pthread_sigmask(SIG_SETMASK, &fullset, NULL);
150 pthread_mutex_lock(&spc->spc_mtx);
151 }
152 } else {
153 spc->spc_istatus = SPCSTATUS_WANTED;
154 pthread_cond_wait(&rw->rw_cv, &spc->spc_mtx);
155 }
156 }
157 TAILQ_REMOVE(&spc->spc_respwait, rw, rw_entries);
158 pthread_mutex_unlock(&spc->spc_mtx);
159 pthread_cond_destroy(&rw->rw_cv);
160
161 if (spc->spc_state == SPCSTATE_DYING)
162 return ENOTCONN;
163 return rw->rw_error;
164 }
165
166
167 static int
168 syscall_req(struct spclient *spc, int sysnum,
169 const void *data, size_t dlen, void **resp)
170 {
171 struct rsp_hdr rhdr;
172 struct respwait rw;
173 sigset_t omask;
174 int rv;
175
176 rhdr.rsp_len = sizeof(rhdr) + dlen;
177 rhdr.rsp_class = RUMPSP_REQ;
178 rhdr.rsp_type = RUMPSP_SYSCALL;
179 rhdr.rsp_sysnum = sysnum;
180
181 pthread_sigmask(SIG_SETMASK, &fullset, &omask);
182 do {
183
184 putwait(spc, &rw, &rhdr);
185 rv = dosend(spc, &rhdr, sizeof(rhdr));
186 rv = dosend(spc, data, dlen);
187 if (rv) {
188 unputwait(spc, &rw);
189 pthread_sigmask(SIG_SETMASK, &omask, NULL);
190 return rv;
191 }
192
193 rv = waitresp(spc, &rw, &omask);
194 } while (rv == EAGAIN);
195 pthread_sigmask(SIG_SETMASK, &omask, NULL);
196
197 *resp = rw.rw_data;
198 return rv;
199 }
200
201 static int
202 handshake_req(struct spclient *spc, uint32_t *auth, int cancel)
203 {
204 struct handshake_fork rf;
205 struct rsp_hdr rhdr;
206 struct respwait rw;
207 sigset_t omask;
208 int rv;
209
210 /* performs server handshake */
211 rhdr.rsp_len = sizeof(rhdr) + (auth ? sizeof(rf) : 0);
212 rhdr.rsp_class = RUMPSP_REQ;
213 rhdr.rsp_type = RUMPSP_HANDSHAKE;
214 if (auth)
215 rhdr.rsp_handshake = HANDSHAKE_FORK;
216 else
217 rhdr.rsp_handshake = HANDSHAKE_GUEST;
218
219 pthread_sigmask(SIG_SETMASK, &fullset, &omask);
220 putwait(spc, &rw, &rhdr);
221 rv = dosend(spc, &rhdr, sizeof(rhdr));
222 if (auth) {
223 memcpy(rf.rf_auth, auth, AUTHLEN*sizeof(*auth));
224 rf.rf_cancel = cancel;
225 rv = dosend(spc, &rf, sizeof(rf));
226 }
227 if (rv != 0 || cancel) {
228 unputwait(spc, &rw);
229 pthread_sigmask(SIG_SETMASK, &omask, NULL);
230 return rv;
231 }
232
233 rv = waitresp(spc, &rw, &omask);
234 pthread_sigmask(SIG_SETMASK, &omask, NULL);
235 if (rv)
236 return rv;
237
238 rv = *(int *)rw.rw_data;
239 free(rw.rw_data);
240
241 return rv;
242 }
243
244 static int
245 prefork_req(struct spclient *spc, void **resp)
246 {
247 struct rsp_hdr rhdr;
248 struct respwait rw;
249 sigset_t omask;
250 int rv;
251
252 rhdr.rsp_len = sizeof(rhdr);
253 rhdr.rsp_class = RUMPSP_REQ;
254 rhdr.rsp_type = RUMPSP_PREFORK;
255 rhdr.rsp_error = 0;
256
257 pthread_sigmask(SIG_SETMASK, &fullset, &omask);
258 putwait(spc, &rw, &rhdr);
259 rv = dosend(spc, &rhdr, sizeof(rhdr));
260 if (rv != 0) {
261 unputwait(spc, &rw);
262 pthread_sigmask(SIG_SETMASK, &omask, NULL);
263 return rv;
264 }
265
266 rv = waitresp(spc, &rw, &omask);
267 pthread_sigmask(SIG_SETMASK, &omask, NULL);
268 *resp = rw.rw_data;
269 return rv;
270 }
271
272 static int
273 send_copyin_resp(struct spclient *spc, uint64_t reqno, void *data, size_t dlen,
274 int wantstr)
275 {
276 struct rsp_hdr rhdr;
277 int rv;
278
279 if (wantstr)
280 dlen = MIN(dlen, strlen(data)+1);
281
282 rhdr.rsp_len = sizeof(rhdr) + dlen;
283 rhdr.rsp_reqno = reqno;
284 rhdr.rsp_class = RUMPSP_RESP;
285 rhdr.rsp_type = RUMPSP_COPYIN;
286 rhdr.rsp_sysnum = 0;
287
288 sendlock(spc);
289 rv = dosend(spc, &rhdr, sizeof(rhdr));
290 rv = dosend(spc, data, dlen);
291 sendunlock(spc);
292
293 return rv;
294 }
295
296 static int
297 send_anonmmap_resp(struct spclient *spc, uint64_t reqno, void *addr)
298 {
299 struct rsp_hdr rhdr;
300 int rv;
301
302 rhdr.rsp_len = sizeof(rhdr) + sizeof(addr);
303 rhdr.rsp_reqno = reqno;
304 rhdr.rsp_class = RUMPSP_RESP;
305 rhdr.rsp_type = RUMPSP_ANONMMAP;
306 rhdr.rsp_sysnum = 0;
307
308 sendlock(spc);
309 rv = dosend(spc, &rhdr, sizeof(rhdr));
310 rv = dosend(spc, &addr, sizeof(addr));
311 sendunlock(spc);
312
313 return rv;
314 }
315
316 int
317 rumpclient_syscall(int sysnum, const void *data, size_t dlen,
318 register_t *retval)
319 {
320 struct rsp_sysresp *resp;
321 void *rdata;
322 int rv;
323
324 DPRINTF(("rumpsp syscall_req: syscall %d with %p/%zu\n",
325 sysnum, data, dlen));
326
327 rv = syscall_req(&clispc, sysnum, data, dlen, &rdata);
328 if (rv)
329 return rv;
330
331 resp = rdata;
332 DPRINTF(("rumpsp syscall_resp: syscall %d error %d, rv: %d/%d\n",
333 sysnum, rv, resp->rsys_retval[0], resp->rsys_retval[1]));
334
335 memcpy(retval, &resp->rsys_retval, sizeof(resp->rsys_retval));
336 rv = resp->rsys_error;
337 free(rdata);
338
339 return rv;
340 }
341
342 static void
343 handlereq(struct spclient *spc)
344 {
345 struct rsp_copydata *copydata;
346 void *mapaddr;
347 size_t maplen;
348 int reqtype = spc->spc_hdr.rsp_type;
349
350 switch (reqtype) {
351 case RUMPSP_COPYIN:
352 case RUMPSP_COPYINSTR:
353 /*LINTED*/
354 copydata = (struct rsp_copydata *)spc->spc_buf;
355 DPRINTF(("rump_sp handlereq: copyin request: %p/%zu\n",
356 copydata->rcp_addr, copydata->rcp_len));
357 send_copyin_resp(spc, spc->spc_hdr.rsp_reqno,
358 copydata->rcp_addr, copydata->rcp_len,
359 reqtype == RUMPSP_COPYINSTR);
360 break;
361 case RUMPSP_COPYOUT:
362 case RUMPSP_COPYOUTSTR:
363 /*LINTED*/
364 copydata = (struct rsp_copydata *)spc->spc_buf;
365 DPRINTF(("rump_sp handlereq: copyout request: %p/%zu\n",
366 copydata->rcp_addr, copydata->rcp_len));
367 /*LINTED*/
368 memcpy(copydata->rcp_addr, copydata->rcp_data,
369 copydata->rcp_len);
370 break;
371 case RUMPSP_ANONMMAP:
372 /*LINTED*/
373 maplen = *(size_t *)spc->spc_buf;
374 mapaddr = mmap(NULL, maplen, PROT_READ|PROT_WRITE,
375 MAP_ANON, -1, 0);
376 if (mapaddr == MAP_FAILED)
377 mapaddr = NULL;
378 DPRINTF(("rump_sp handlereq: anonmmap: %p\n", mapaddr));
379 send_anonmmap_resp(spc, spc->spc_hdr.rsp_reqno, mapaddr);
380 break;
381 default:
382 printf("PANIC: INVALID TYPE %d\n", reqtype);
383 abort();
384 break;
385 }
386
387 spcfreebuf(spc);
388 }
389
390 static unsigned ptab_idx;
391 static struct sockaddr *serv_sa;
392
393 static int
394 doconnect(void)
395 {
396 struct kevent kev[NSIG+1];
397 char banner[MAXBANNER];
398 int s, error, flags, i;
399 ssize_t n;
400
401 s = host_socket(parsetab[ptab_idx].domain, SOCK_STREAM, 0);
402 if (s == -1)
403 return -1;
404
405 if (host_connect(s, serv_sa, (socklen_t)serv_sa->sa_len) == -1) {
406 error = errno;
407 fprintf(stderr, "rump_sp: client connect failed\n");
408 errno = error;
409 return -1;
410 }
411
412 if ((error = parsetab[ptab_idx].connhook(s)) != 0) {
413 error = errno;
414 fprintf(stderr, "rump_sp: connect hook failed\n");
415 errno = error;
416 return -1;
417 }
418
419 if ((n = host_read(s, banner, sizeof(banner)-1)) < 0) {
420 error = errno;
421 fprintf(stderr, "rump_sp: failed to read banner\n");
422 errno = error;
423 return -1;
424 }
425
426 if (banner[n-1] != '\n') {
427 fprintf(stderr, "rump_sp: invalid banner\n");
428 errno = EINVAL;
429 return -1;
430 }
431 banner[n] = '\0';
432
433 flags = host_fcntl(s, F_GETFL, 0);
434 if (host_fcntl(s, F_SETFL, flags | O_NONBLOCK) == -1) {
435 fprintf(stderr, "rump_sp: cannot set socket fd to nonblock\n");
436 errno = EINVAL;
437 return -1;
438 }
439
440 /* parse the banner some day */
441
442 /* setup kqueue, we want all signals and the fd */
443 if ((kq = kqueue()) == -1) {
444 error = errno;
445 fprintf(stderr, "rump_sp: cannot setup kqueue");
446 errno = error;
447 return -1;
448 }
449
450 for (i = 0; i < NSIG; i++) {
451 EV_SET(&kev[i], i+1, EVFILT_SIGNAL, EV_ADD|EV_ENABLE, 0, 0, 0);
452 }
453 EV_SET(&kev[NSIG], s, EVFILT_READ, EV_ADD|EV_ENABLE, 0, 0, 0);
454 if (kevent(kq, kev, NSIG+1, NULL, 0, NULL) == -1) {
455 error = errno;
456 fprintf(stderr, "rump_sp: kevent() failed");
457 errno = error;
458 return -1;
459 }
460
461 clispc.spc_fd = s;
462 TAILQ_INIT(&clispc.spc_respwait);
463 pthread_mutex_init(&clispc.spc_mtx, NULL);
464 pthread_cond_init(&clispc.spc_cv, NULL);
465
466 return 0;
467 }
468
469 void *(*rumpclient_dlsym)(void *, const char *);
470
471 int
472 rumpclient_init()
473 {
474 char *p;
475 int error;
476
477 /* dlsym overrided by rumphijack? */
478 if (!rumpclient_dlsym)
479 rumpclient_dlsym = dlsym;
480
481 /*
482 * sag mir, wo die symbol sind. zogen fort, der krieg beginnt.
483 * wann wird man je verstehen? wann wird man je verstehen?
484 */
485 #define FINDSYM2(_name_,_syscall_) \
486 if ((host_##_name_ = rumpclient_dlsym(RTLD_NEXT, \
487 #_syscall_)) == NULL) \
488 /* host_##_name_ = _syscall_ */;
489 #define FINDSYM(_name_) FINDSYM2(_name_,_name_)
490 FINDSYM2(socket,__socket30);
491 FINDSYM(close);
492 FINDSYM(connect);
493 FINDSYM(fcntl);
494 FINDSYM(poll);
495 FINDSYM(pollts);
496 FINDSYM(read);
497 FINDSYM(sendto);
498 FINDSYM(setsockopt);
499 #undef FINDSYM
500 #undef FINDSY2
501
502 if ((p = getenv("RUMP_SERVER")) == NULL) {
503 errno = ENOENT;
504 return -1;
505 }
506
507 if ((error = parseurl(p, &serv_sa, &ptab_idx, 0)) != 0) {
508 errno = error;
509 return -1;
510 }
511
512 if (doconnect() == -1)
513 return -1;
514
515 error = handshake_req(&clispc, NULL, 0);
516 if (error) {
517 pthread_mutex_destroy(&clispc.spc_mtx);
518 pthread_cond_destroy(&clispc.spc_cv);
519 host_close(clispc.spc_fd);
520 errno = error;
521 return -1;
522 }
523
524 sigfillset(&fullset);
525 return 0;
526 }
527
528 struct rumpclient_fork {
529 uint32_t fork_auth[AUTHLEN];
530 };
531
532 struct rumpclient_fork *
533 rumpclient_prefork(void)
534 {
535 struct rumpclient_fork *rpf;
536 void *resp;
537 int rv;
538
539 rpf = malloc(sizeof(*rpf));
540 if (rpf == NULL)
541 return NULL;
542
543 if ((rv = prefork_req(&clispc, &resp)) != 0) {
544 free(rpf);
545 errno = rv;
546 return NULL;
547 }
548
549 memcpy(rpf->fork_auth, resp, sizeof(rpf->fork_auth));
550 free(resp);
551
552 return rpf;
553 }
554
555 int
556 rumpclient_fork_init(struct rumpclient_fork *rpf)
557 {
558 int error;
559
560 host_close(clispc.spc_fd);
561 host_close(kq);
562 kq = -1;
563 memset(&clispc, 0, sizeof(clispc));
564 clispc.spc_fd = -1;
565
566 if (doconnect() == -1)
567 return -1;
568
569 error = handshake_req(&clispc, rpf->fork_auth, 0);
570 if (error) {
571 pthread_mutex_destroy(&clispc.spc_mtx);
572 pthread_cond_destroy(&clispc.spc_cv);
573 errno = error;
574 return -1;
575 }
576
577 return 0;
578 }
579