rumpuser_sp.c revision 1.9 1 /* $NetBSD: rumpuser_sp.c,v 1.9 2010/11/19 17:47: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 * Sysproxy routines. This provides system RPC support over host sockets.
30 * The most notable limitation is that the client and server must share
31 * the same ABI. This does not mean that they have to be the same
32 * machine or that they need to run the same version of the host OS,
33 * just that they must agree on the data structures. This even *might*
34 * work correctly from one hardware architecture to another.
35 *
36 * Not finished yet, i.e. don't use in production. Lacks locking plus
37 * handling of multiple clients and unexpected connection closes.
38 */
39
40 #include <sys/cdefs.h>
41 __RCSID("$NetBSD: rumpuser_sp.c,v 1.9 2010/11/19 17:47:44 pooka Exp $");
42
43 #include <sys/types.h>
44 #include <sys/mman.h>
45 #include <sys/socket.h>
46
47 #include <arpa/inet.h>
48 #include <netinet/in.h>
49 #include <netinet/tcp.h>
50
51 #include <assert.h>
52 #include <errno.h>
53 #include <fcntl.h>
54 #include <poll.h>
55 #include <pthread.h>
56 #include <stdarg.h>
57 #include <stdio.h>
58 #include <stdlib.h>
59 #include <string.h>
60 #include <unistd.h>
61
62 #include <rump/rumpuser.h>
63
64 #include "sp_common.c"
65
66 #define MAXCLI 4
67
68 static struct pollfd pfdlist[MAXCLI];
69 static struct spclient spclist[MAXCLI];
70 static unsigned int nfds, maxidx;
71 static pthread_key_t spclient_tls;
72
73 static struct rumpuser_sp_ops spops;
74
75 /*
76 * Manual wrappers, since librump does not have access to the
77 * user namespace wrapped interfaces.
78 */
79
80 static void
81 lwproc_switch(struct lwp *l)
82 {
83
84 spops.spop_schedule();
85 spops.spop_lwproc_switch(l);
86 spops.spop_unschedule();
87 }
88
89 static void
90 lwproc_release(void)
91 {
92
93 spops.spop_schedule();
94 spops.spop_lwproc_release();
95 spops.spop_unschedule();
96 }
97
98 static int
99 lwproc_newproc(void)
100 {
101 int rv;
102
103 spops.spop_schedule();
104 rv = spops.spop_lwproc_newproc();
105 spops.spop_unschedule();
106
107 return rv;
108 }
109
110 static int
111 lwproc_newlwp(pid_t pid)
112 {
113 int rv;
114
115 spops.spop_schedule();
116 rv = spops.spop_lwproc_newlwp(pid);
117 spops.spop_unschedule();
118
119 return rv;
120 }
121
122 static struct lwp *
123 lwproc_curlwp(void)
124 {
125 struct lwp *l;
126
127 spops.spop_schedule();
128 l = spops.spop_lwproc_curlwp();
129 spops.spop_unschedule();
130
131 return l;
132 }
133
134 static pid_t
135 lwproc_getpid(void)
136 {
137 pid_t p;
138
139 spops.spop_schedule();
140 p = spops.spop_getpid();
141 spops.spop_unschedule();
142
143 return p;
144 }
145
146 static int
147 rumpsyscall(int sysnum, void *data, register_t *retval)
148 {
149 int rv;
150
151 spops.spop_schedule();
152 rv = spops.spop_syscall(sysnum, data, retval);
153 spops.spop_unschedule();
154
155 return rv;
156 }
157
158 static uint64_t
159 nextreq(struct spclient *spc)
160 {
161 uint64_t nw;
162
163 pthread_mutex_lock(&spc->spc_mtx);
164 nw = spc->spc_nextreq++;
165 pthread_mutex_unlock(&spc->spc_mtx);
166
167 return nw;
168 }
169
170 static int
171 send_syscall_resp(struct spclient *spc, uint64_t reqno, int error,
172 register_t *retval)
173 {
174 struct rsp_hdr rhdr;
175 struct rsp_sysresp sysresp;
176 int rv;
177
178 rhdr.rsp_len = sizeof(rhdr) + sizeof(sysresp);
179 rhdr.rsp_reqno = reqno;
180 rhdr.rsp_class = RUMPSP_RESP;
181 rhdr.rsp_type = RUMPSP_SYSCALL;
182 rhdr.rsp_sysnum = 0;
183
184 sysresp.rsys_error = error;
185 memcpy(sysresp.rsys_retval, retval, sizeof(sysresp.rsys_retval));
186
187 sendlock(spc);
188 rv = dosend(spc, &rhdr, sizeof(rhdr));
189 rv = dosend(spc, &sysresp, sizeof(sysresp));
190 sendunlock(spc);
191
192 return rv;
193 }
194
195 static int
196 copyin_req(struct spclient *spc, const void *remaddr, size_t dlen, void **resp)
197 {
198 struct rsp_hdr rhdr;
199 struct rsp_copydata copydata;
200 struct respwait rw;
201 int rv;
202
203 DPRINTF(("copyin_req: %zu bytes from %p\n", dlen, remaddr));
204
205 rhdr.rsp_len = sizeof(rhdr) + sizeof(copydata);
206 rhdr.rsp_class = RUMPSP_REQ;
207 rhdr.rsp_type = RUMPSP_COPYIN;
208 rhdr.rsp_sysnum = 0;
209
210 copydata.rcp_addr = __UNCONST(remaddr);
211 copydata.rcp_len = dlen;
212
213 putwait(spc, &rw, &rhdr);
214
215 sendlock(spc);
216 rv = dosend(spc, &rhdr, sizeof(rhdr));
217 rv = dosend(spc, ©data, sizeof(copydata));
218 sendunlock(spc);
219 if (rv)
220 return rv; /* XXX: unputwait */
221
222 rv = waitresp(spc, &rw);
223
224 DPRINTF(("copyin: response %d\n", rv));
225
226 *resp = rw.rw_data;
227 return rv;
228
229 }
230
231 static int
232 send_copyout_req(struct spclient *spc, const void *remaddr,
233 const void *data, size_t dlen)
234 {
235 struct rsp_hdr rhdr;
236 struct rsp_copydata copydata;
237 int rv;
238
239 DPRINTF(("copyout_req (async): %zu bytes to %p\n", dlen, remaddr));
240
241 rhdr.rsp_len = sizeof(rhdr) + sizeof(copydata) + dlen;
242 rhdr.rsp_reqno = nextreq(spc);
243 rhdr.rsp_class = RUMPSP_REQ;
244 rhdr.rsp_type = RUMPSP_COPYOUT;
245 rhdr.rsp_sysnum = 0;
246
247 copydata.rcp_addr = __UNCONST(remaddr);
248 copydata.rcp_len = dlen;
249
250 sendlock(spc);
251 rv = dosend(spc, &rhdr, sizeof(rhdr));
252 rv = dosend(spc, ©data, sizeof(copydata));
253 rv = dosend(spc, data, dlen);
254 sendunlock(spc);
255
256 return rv;
257 }
258
259 static int
260 anonmmap_req(struct spclient *spc, size_t howmuch, void **resp)
261 {
262 struct rsp_hdr rhdr;
263 struct respwait rw;
264 int rv;
265
266 DPRINTF(("anonmmap_req: %zu bytes\n", howmuch));
267
268 rhdr.rsp_len = sizeof(rhdr) + sizeof(howmuch);
269 rhdr.rsp_class = RUMPSP_REQ;
270 rhdr.rsp_type = RUMPSP_ANONMMAP;
271 rhdr.rsp_sysnum = 0;
272
273 putwait(spc, &rw, &rhdr);
274
275 sendlock(spc);
276 rv = dosend(spc, &rhdr, sizeof(rhdr));
277 rv = dosend(spc, &howmuch, sizeof(howmuch));
278 sendunlock(spc);
279 if (rv)
280 return rv; /* XXX: unputwait */
281
282 rv = waitresp(spc, &rw);
283 *resp = rw.rw_data;
284
285 DPRINTF(("anonmmap: mapped at %p\n", **(void ***)resp));
286
287 return rv;
288 }
289
290 static void
291 serv_handledisco(unsigned int idx)
292 {
293 struct spclient *spc = &spclist[idx];
294 int fd = spc->spc_fd;
295
296 DPRINTF(("rump_sp: disconnecting [%u]\n", idx));
297
298 lwproc_switch(spc->spc_mainlwp);
299 lwproc_release();
300
301 pthread_mutex_destroy(&spc->spc_mtx);
302 pthread_cond_destroy(&spc->spc_cv);
303 free(spc->spc_buf);
304 memset(spc, 0, sizeof(*spc));
305 close(fd);
306 pfdlist[idx].fd = -1;
307 nfds--;
308
309 if (idx == maxidx) {
310 while (idx--) {
311 if (pfdlist[idx].fd != -1) {
312 maxidx = idx;
313 break;
314 }
315 assert(idx != 0);
316 }
317 DPRINTF(("rump_sp: set maxidx to [%u]\n", maxidx));
318 }
319 }
320
321 static int
322 serv_handleconn(int fd, connecthook_fn connhook)
323 {
324 struct sockaddr_storage ss;
325 socklen_t sl = sizeof(ss);
326 int newfd, flags, error;
327 unsigned i;
328
329 /*LINTED: cast ok */
330 newfd = accept(fd, (struct sockaddr *)&ss, &sl);
331 if (newfd == -1)
332 return errno;
333
334 /* XXX: should do some sort of handshake too */
335
336 if (nfds == MAXCLI) {
337 close(newfd); /* EBUSY */
338 return EBUSY;
339 }
340
341 flags = fcntl(newfd, F_GETFL, 0);
342 if (fcntl(newfd, F_SETFL, flags | O_NONBLOCK) == -1) {
343 close(newfd);
344 return errno;
345 }
346 flags = 1;
347
348 if ((error = connhook(newfd)) != 0) {
349 close(newfd);
350 return error;
351 }
352
353 if ((error = lwproc_newproc()) != 0) {
354 close(newfd);
355 return error;
356 }
357
358 /* find empty slot the simple way */
359 for (i = 0; i < MAXCLI; i++) {
360 if (pfdlist[i].fd == -1)
361 break;
362 }
363
364 assert(i < MAXCLI);
365 nfds++;
366
367 pfdlist[i].fd = newfd;
368 spclist[i].spc_fd = newfd;
369 spclist[i].spc_mainlwp = lwproc_curlwp();
370 spclist[i].spc_istatus = SPCSTATUS_BUSY; /* dedicated receiver */
371 spclist[i].spc_pid = lwproc_getpid();
372
373 TAILQ_INIT(&spclist[i].spc_respwait);
374 pthread_mutex_init(&spclist[i].spc_mtx, NULL);
375 pthread_cond_init(&spclist[i].spc_cv, NULL);
376
377 if (maxidx < i)
378 maxidx = i;
379
380 DPRINTF(("rump_sp: added new connection at idx %u, pid %d\n",
381 i, lwproc_getpid()));
382
383 lwproc_switch(NULL);
384
385 return 0;
386 }
387
388 static void
389 serv_handlesyscall(struct spclient *spc, struct rsp_hdr *rhdr, uint8_t *data)
390 {
391 register_t retval[2] = {0, 0};
392 int rv, sysnum;
393
394 sysnum = (int)rhdr->rsp_sysnum;
395 DPRINTF(("rump_sp: handling syscall %d from client %d\n",
396 sysnum, 0));
397
398 pthread_setspecific(spclient_tls, spc);
399 lwproc_newlwp(spc->spc_pid);
400 rv = rumpsyscall(sysnum, data, retval);
401 lwproc_switch(NULL);
402 pthread_setspecific(spclient_tls, NULL);
403 free(data);
404
405 DPRINTF(("rump_sp: got return value %d & %d/%d\n",
406 rv, retval[0], retval[1]));
407
408 send_syscall_resp(spc, rhdr->rsp_reqno, rv, retval);
409 }
410
411 struct sysbouncearg {
412 struct spclient *sba_spc;
413 struct rsp_hdr sba_hdr;
414 uint8_t *sba_data;
415 };
416 static void *
417 serv_syscallbouncer(void *arg)
418 {
419 struct sysbouncearg *barg = arg;
420
421 serv_handlesyscall(barg->sba_spc, &barg->sba_hdr, barg->sba_data);
422 free(arg);
423 return NULL;
424 }
425
426 int
427 rumpuser_sp_copyin(const void *uaddr, void *kaddr, size_t len)
428 {
429 struct spclient *spc;
430 void *rdata = NULL; /* XXXuninit */
431
432 spc = pthread_getspecific(spclient_tls);
433 if (!spc)
434 return EFAULT;
435
436 copyin_req(spc, uaddr, len, &rdata);
437
438 memcpy(kaddr, rdata, len);
439 free(rdata);
440
441 return 0;
442 }
443
444 int
445 rumpuser_sp_copyout(const void *kaddr, void *uaddr, size_t dlen)
446 {
447 struct spclient *spc;
448
449 spc = pthread_getspecific(spclient_tls);
450 if (!spc) {
451 DPRINTF(("rump_sp: copyout curlwp not found\n"));
452 return EFAULT;
453 }
454
455 if (send_copyout_req(spc, uaddr, kaddr, dlen) != 0)
456 return EFAULT;
457 return 0;
458 }
459
460 int
461 rumpuser_sp_anonmmap(size_t howmuch, void **addr)
462 {
463 struct spclient *spc;
464 void *resp, *rdata;
465 int rv;
466
467 spc = pthread_getspecific(spclient_tls);
468 if (!spc)
469 return EFAULT;
470
471 rv = anonmmap_req(spc, howmuch, &rdata);
472 if (rv)
473 return rv;
474
475 resp = *(void **)rdata;
476 free(rdata);
477
478 if (resp == NULL) {
479 return ENOMEM;
480 }
481
482 *addr = resp;
483 return 0;
484 }
485
486 /*
487 *
488 * Startup routines and mainloop for server.
489 *
490 */
491
492 struct spservarg {
493 int sps_sock;
494 connecthook_fn sps_connhook;
495 };
496
497 static void
498 handlereq(struct spclient *spc)
499 {
500 struct sysbouncearg *sba;
501 pthread_attr_t pattr;
502 pthread_t pt;
503 int rv;
504
505 /* XXX: check that it's a syscall */
506
507 sba = malloc(sizeof(*sba));
508 if (sba == NULL) {
509 /* panic */
510 abort();
511 }
512
513 sba->sba_spc = spc;
514 sba->sba_hdr = spc->spc_hdr;
515 sba->sba_data = spc->spc_buf;
516
517 spc->spc_buf = NULL;
518 spc->spc_off = 0;
519
520 pthread_attr_init(&pattr);
521 pthread_attr_setdetachstate(&pattr, 1);
522
523 if ((rv = pthread_create(&pt, &pattr, serv_syscallbouncer, sba)) != 0) {
524 /* panic */
525 abort();
526 }
527 }
528
529 static void *
530 spserver(void *arg)
531 {
532 struct spservarg *sarg = arg;
533 unsigned idx;
534 int seen;
535 int rv;
536
537 for (idx = 1; idx < MAXCLI; idx++) {
538 pfdlist[idx].fd = -1;
539 pfdlist[idx].events = POLLIN;
540 }
541 pfdlist[0].fd = sarg->sps_sock;
542 pfdlist[0].events = POLLIN;
543 nfds = 1;
544 maxidx = 0;
545
546 DPRINTF(("rump_sp: server mainloop\n"));
547
548 for (;;) {
549 DPRINTF(("rump_sp: loop nfd %d\n", maxidx+1));
550 seen = 0;
551 rv = poll(pfdlist, maxidx+1, INFTIM);
552 assert(maxidx+1 <= MAXCLI);
553 assert(rv != 0);
554 if (rv == -1) {
555 if (errno == EINTR)
556 continue;
557 fprintf(stderr, "rump_spserver: poll returned %d\n",
558 errno);
559 break;
560 }
561
562 for (idx = 0; seen < rv; idx++) {
563 assert(idx < MAXCLI);
564
565 if ((pfdlist[idx].revents & POLLIN) == 0)
566 continue;
567
568 seen++;
569 DPRINTF(("rump_sp: activity at [%u] %d/%d\n",
570 idx, seen, rv));
571 if (idx > 0) {
572 struct spclient *spc = &spclist[idx];
573
574 DPRINTF(("rump_sp: mainloop read [%u]\n", idx));
575 switch (readframe(spc)) {
576 case 0:
577 break;
578 case -1:
579 serv_handledisco(idx);
580 break;
581 default:
582 switch (spc->spc_hdr.rsp_class) {
583 case RUMPSP_RESP:
584 kickwaiter(spc);
585 break;
586 case RUMPSP_REQ:
587 handlereq(spc);
588 break;
589 default:
590 printf("PANIC\n");
591 abort();
592 break;
593 }
594 break;
595 }
596 } else {
597 DPRINTF(("rump_sp: mainloop new connection\n"));
598 serv_handleconn(pfdlist[0].fd,
599 sarg->sps_connhook);
600 }
601 }
602 }
603
604 return NULL;
605 }
606
607 int
608 rumpuser_sp_init(const struct rumpuser_sp_ops *spopsp, const char *url)
609 {
610 pthread_t pt;
611 struct spservarg *sarg;
612 struct sockaddr *sap;
613 char *p;
614 unsigned idx;
615 int error, s;
616
617 p = strdup(url);
618 if (p == NULL)
619 return ENOMEM;
620 error = parseurl(p, &sap, &idx, 1);
621 free(p);
622 if (error)
623 return error;
624
625 s = socket(parsetab[idx].domain, SOCK_STREAM, 0);
626 if (s == -1)
627 return errno;
628
629 spops = *spopsp;
630 sarg = malloc(sizeof(*sarg));
631 if (sarg == NULL) {
632 close(s);
633 return ENOMEM;
634 }
635
636 sarg->sps_sock = s;
637 sarg->sps_connhook = parsetab[idx].connhook;
638
639 /* sloppy error recovery */
640
641 error = pthread_key_create(&spclient_tls, NULL);
642 if (error) {
643 fprintf(stderr, "rump_sp: tls create failed\n");
644 return error;
645 }
646
647 /*LINTED*/
648 if (bind(s, sap, sap->sa_len) == -1) {
649 fprintf(stderr, "rump_sp: server bind failed\n");
650 return errno;
651 }
652 if (listen(s, 20) == -1) {
653 fprintf(stderr, "rump_sp: server listen failed\n");
654 return errno;
655 }
656
657 if ((error = pthread_create(&pt, NULL, spserver, sarg)) != 0) {
658 fprintf(stderr, "rump_sp: cannot create wrkr thread\n");
659 return errno;
660 }
661 pthread_detach(pt);
662
663 return 0;
664 }
665