rumpuser.c revision 1.6 1 /* $NetBSD: rumpuser.c,v 1.6 2010/06/02 18:15:35 pooka Exp $ */
2
3 /*
4 * Copyright (c) 2007-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 #include <sys/cdefs.h>
29 #if !defined(lint)
30 __RCSID("$NetBSD: rumpuser.c,v 1.6 2010/06/02 18:15:35 pooka Exp $");
31 #endif /* !lint */
32
33 /* thank the maker for this */
34 #ifdef __linux__
35 #define _XOPEN_SOURCE 500
36 #define _BSD_SOURCE
37 #define _FILE_OFFSET_BITS 64
38 #include <features.h>
39 #endif
40
41 #include <sys/param.h>
42 #include <sys/event.h>
43 #include <sys/ioctl.h>
44 #include <sys/mman.h>
45 #include <sys/uio.h>
46
47 #ifdef __NetBSD__
48 #include <sys/disklabel.h>
49 #include <sys/sysctl.h>
50 #endif
51
52 #include <assert.h>
53 #include <err.h>
54 #include <errno.h>
55 #include <fcntl.h>
56 #include <poll.h>
57 #include <signal.h>
58 #include <stdarg.h>
59 #include <stdint.h>
60 #include <stdio.h>
61 #include <stdlib.h>
62 #include <string.h>
63 #include <time.h>
64 #include <unistd.h>
65
66 #include <rump/rumpuser.h>
67
68 #include "rumpuser_int.h"
69
70 int
71 rumpuser_getfileinfo(const char *path, uint64_t *sizep, int *ftp, int *error)
72 {
73 struct stat sb;
74 uint64_t size;
75 int needsdev = 0, rv = 0, ft;
76
77 if (stat(path, &sb) == -1) {
78 *error = errno;
79 return -1;
80 }
81
82 switch (sb.st_mode & S_IFMT) {
83 case S_IFDIR:
84 ft = RUMPUSER_FT_DIR;
85 break;
86 case S_IFREG:
87 ft = RUMPUSER_FT_REG;
88 break;
89 case S_IFBLK:
90 ft = RUMPUSER_FT_BLK;
91 needsdev = 1;
92 break;
93 case S_IFCHR:
94 ft = RUMPUSER_FT_CHR;
95 needsdev = 1;
96 break;
97 default:
98 ft = RUMPUSER_FT_OTHER;
99 break;
100 }
101
102 if (!needsdev) {
103 size = sb.st_size;
104 } else if (sizep) {
105 /*
106 * Welcome to the jungle. Of course querying the kernel
107 * for a device partition size is supposed to be far from
108 * trivial. On NetBSD we use ioctl. On $other platform
109 * we have a problem. We try "the lseek trick" and just
110 * fail if that fails. Platform specific code can later
111 * be written here if appropriate.
112 *
113 * On NetBSD we hope and pray that for block devices nobody
114 * else is holding them open, because otherwise the kernel
115 * will not permit us to open it. Thankfully, this is
116 * usually called only in bootstrap and then we can
117 * forget about it.
118 */
119 #ifndef __NetBSD__
120 off_t off;
121 int fd;
122
123 fd = open(path, O_RDONLY);
124 if (fd == -1) {
125 *error = errno;
126 rv = -1;
127 goto out;
128 }
129
130 off = lseek(fd, 0, SEEK_END);
131 close(fd);
132 if (off != 0) {
133 size = off;
134 goto out;
135 }
136 fprintf(stderr, "error: device size query not implemented on "
137 "this platform\n");
138 *error = EOPNOTSUPP;
139 rv = -1;
140 goto out;
141 #else
142 struct disklabel lab;
143 struct partition *parta;
144 int fd;
145
146 fd = open(path, O_RDONLY);
147 if (fd == -1) {
148 *error = errno;
149 rv = -1;
150 goto out;
151 }
152
153 if (ioctl(fd, DIOCGDINFO, &lab) == -1) {
154 *error = errno;
155 rv = -1;
156 goto out;
157 }
158 close(fd);
159
160 parta = &lab.d_partitions[DISKPART(sb.st_rdev)];
161 size = (uint64_t)lab.d_secsize * parta->p_size;
162 #endif /* __NetBSD__ */
163 }
164
165 out:
166 if (rv == 0 && sizep)
167 *sizep = size;
168 if (rv == 0 && ftp)
169 *ftp = ft;
170
171 return rv;
172 }
173
174 int
175 rumpuser_nanosleep(uint64_t *sec, uint64_t *nsec, int *error)
176 {
177 struct timespec rqt, rmt;
178 int rv;
179
180 /*LINTED*/
181 rqt.tv_sec = *sec;
182 /*LINTED*/
183 rqt.tv_nsec = *nsec;
184
185 KLOCK_WRAP(rv = nanosleep(&rqt, &rmt));
186 if (rv == -1)
187 *error = errno;
188
189 *sec = rmt.tv_sec;
190 *nsec = rmt.tv_nsec;
191
192 return rv;
193 }
194
195 void *
196 rumpuser_malloc(size_t howmuch, int alignment)
197 {
198 void *mem;
199 int rv;
200
201 if (alignment == 0)
202 alignment = sizeof(void *);
203
204 rv = posix_memalign(&mem, alignment, howmuch);
205 if (__predict_false(rv != 0)) {
206 if (rv == EINVAL) {
207 printf("rumpuser_malloc: invalid alignment %d\n",
208 alignment);
209 abort();
210 }
211 mem = NULL;
212 }
213
214 return mem;
215 }
216
217 void *
218 rumpuser_realloc(void *ptr, size_t howmuch)
219 {
220
221 return realloc(ptr, howmuch);
222 }
223
224 void
225 rumpuser_free(void *ptr)
226 {
227
228 free(ptr);
229 }
230
231 void *
232 rumpuser_anonmmap(size_t size, int alignbit, int exec, int *error)
233 {
234 void *rv;
235 int prot;
236
237 prot = PROT_READ|PROT_WRITE;
238 if (exec)
239 prot |= PROT_EXEC;
240 /* XXX: MAP_ALIGNED() is not portable */
241 rv = mmap(NULL, size, prot, MAP_ANON | MAP_ALIGNED(alignbit), -1, 0);
242 if (rv == MAP_FAILED) {
243 *error = errno;
244 return NULL;
245 }
246 return rv;
247 }
248
249 void
250 rumpuser_unmap(void *addr, size_t len)
251 {
252 int rv;
253
254 rv = munmap(addr, len);
255 assert(rv == 0);
256 }
257
258 void *
259 rumpuser_filemmap(int fd, off_t offset, size_t len, int flags, int *error)
260 {
261 void *rv;
262 int mmflags, prot;
263
264 if (flags & RUMPUSER_FILEMMAP_TRUNCATE)
265 ftruncate(fd, offset + len);
266
267 mmflags = MAP_FILE;
268 if (flags & RUMPUSER_FILEMMAP_SHARED)
269 mmflags |= MAP_SHARED;
270 else
271 mmflags |= MAP_PRIVATE;
272
273 prot = 0;
274 if (flags & RUMPUSER_FILEMMAP_READ)
275 prot |= PROT_READ;
276 if (flags & RUMPUSER_FILEMMAP_WRITE)
277 prot |= PROT_WRITE;
278
279 rv = mmap(NULL, len, PROT_READ|PROT_WRITE, mmflags, fd, offset);
280 if (rv == MAP_FAILED) {
281 *error = errno;
282 return NULL;
283 }
284
285 *error = 0;
286 return rv;
287 }
288
289 int
290 rumpuser_memsync(void *addr, size_t len, int *error)
291 {
292
293 DOCALL_KLOCK(int, (msync(addr, len, MS_SYNC)));
294 }
295
296 int
297 rumpuser_open(const char *path, int flags, int *error)
298 {
299
300 DOCALL(int, (open(path, flags, 0644)));
301 }
302
303 int
304 rumpuser_ioctl(int fd, u_long cmd, void *data, int *error)
305 {
306
307 DOCALL_KLOCK(int, (ioctl(fd, cmd, data)));
308 }
309
310 int
311 rumpuser_close(int fd, int *error)
312 {
313
314 DOCALL(int, close(fd));
315 }
316
317 int
318 rumpuser_fsync(int fd, int *error)
319 {
320
321 DOCALL_KLOCK(int, fsync(fd));
322 }
323
324 ssize_t
325 rumpuser_read(int fd, void *data, size_t size, int *error)
326 {
327 ssize_t rv;
328
329 KLOCK_WRAP(rv = read(fd, data, size));
330 if (rv == -1)
331 *error = errno;
332
333 return rv;
334 }
335
336 ssize_t
337 rumpuser_pread(int fd, void *data, size_t size, off_t offset, int *error)
338 {
339 ssize_t rv;
340
341 KLOCK_WRAP(rv = pread(fd, data, size, offset));
342 if (rv == -1)
343 *error = errno;
344
345 return rv;
346 }
347
348 void
349 rumpuser_read_bio(int fd, void *data, size_t size, off_t offset,
350 rump_biodone_fn biodone, void *biodonecookie)
351 {
352 ssize_t rv;
353 int error = 0;
354
355 rv = rumpuser_pread(fd, data, size, offset, &error);
356 /* check against <0 instead of ==-1 to get typing below right */
357 if (rv < 0)
358 rv = 0;
359
360 /* LINTED: see above */
361 biodone(biodonecookie, rv, error);
362 }
363
364 ssize_t
365 rumpuser_write(int fd, const void *data, size_t size, int *error)
366 {
367 ssize_t rv;
368
369 KLOCK_WRAP(rv = write(fd, data, size));
370 if (rv == -1)
371 *error = errno;
372
373 return rv;
374 }
375
376 ssize_t
377 rumpuser_pwrite(int fd, const void *data, size_t size, off_t offset, int *error)
378 {
379 ssize_t rv;
380
381 KLOCK_WRAP(rv = pwrite(fd, data, size, offset));
382 if (rv == -1)
383 *error = errno;
384
385 return rv;
386 }
387
388 void
389 rumpuser_write_bio(int fd, const void *data, size_t size, off_t offset,
390 rump_biodone_fn biodone, void *biodonecookie)
391 {
392 ssize_t rv;
393 int error = 0;
394
395 rv = rumpuser_pwrite(fd, data, size, offset, &error);
396 /* check against <0 instead of ==-1 to get typing below right */
397 if (rv < 0)
398 rv = 0;
399
400 /* LINTED: see above */
401 biodone(biodonecookie, rv, error);
402 }
403
404 ssize_t
405 rumpuser_readv(int fd, const struct rumpuser_iovec *riov, int iovcnt,
406 int *error)
407 {
408 struct iovec *iovp;
409 ssize_t rv;
410 int i;
411
412 iovp = malloc(iovcnt * sizeof(struct iovec));
413 if (iovp == NULL) {
414 *error = ENOMEM;
415 return -1;
416 }
417 for (i = 0; i < iovcnt; i++) {
418 iovp[i].iov_base = riov[i].iov_base;
419 /*LINTED*/
420 iovp[i].iov_len = riov[i].iov_len;
421 }
422
423 KLOCK_WRAP(rv = readv(fd, iovp, iovcnt));
424 if (rv == -1)
425 *error = errno;
426 free(iovp);
427
428 return rv;
429 }
430
431 ssize_t
432 rumpuser_writev(int fd, const struct rumpuser_iovec *riov, int iovcnt,
433 int *error)
434 {
435 struct iovec *iovp;
436 ssize_t rv;
437 int i;
438
439 iovp = malloc(iovcnt * sizeof(struct iovec));
440 if (iovp == NULL) {
441 *error = ENOMEM;
442 return -1;
443 }
444 for (i = 0; i < iovcnt; i++) {
445 iovp[i].iov_base = riov[i].iov_base;
446 /*LINTED*/
447 iovp[i].iov_len = riov[i].iov_len;
448 }
449
450 KLOCK_WRAP(rv = writev(fd, iovp, iovcnt));
451 if (rv == -1)
452 *error = errno;
453 free(iovp);
454
455 return rv;
456 }
457
458 int
459 rumpuser_gettime(uint64_t *sec, uint64_t *nsec, int *error)
460 {
461 struct timeval tv;
462 int rv;
463
464 rv = gettimeofday(&tv, NULL);
465 if (rv == -1) {
466 *error = errno;
467 return rv;
468 }
469
470 *sec = tv.tv_sec;
471 *nsec = tv.tv_usec * 1000;
472
473 return 0;
474 }
475
476 int
477 rumpuser_getenv(const char *name, char *buf, size_t blen, int *error)
478 {
479
480 DOCALL(int, getenv_r(name, buf, blen));
481 }
482
483 int
484 rumpuser_gethostname(char *name, size_t namelen, int *error)
485 {
486
487 DOCALL(int, (gethostname(name, namelen)));
488 }
489
490 int
491 rumpuser_poll(struct pollfd *fds, int nfds, int timeout, int *error)
492 {
493
494 DOCALL_KLOCK(int, (poll(fds, (nfds_t)nfds, timeout)));
495 }
496
497 int
498 rumpuser_putchar(int c, int *error)
499 {
500
501 DOCALL(int, (putchar(c)));
502 }
503
504 void
505 rumpuser_exit(int rv)
506 {
507
508 if (rv == RUMPUSER_PANIC)
509 abort();
510 else
511 exit(rv);
512 }
513
514 void
515 rumpuser_seterrno(int error)
516 {
517
518 errno = error;
519 }
520
521 int
522 rumpuser_writewatchfile_setup(int kq, int fd, intptr_t opaque, int *error)
523 {
524 struct kevent kev;
525
526 if (kq == -1) {
527 kq = kqueue();
528 if (kq == -1) {
529 *error = errno;
530 return -1;
531 }
532 }
533
534 EV_SET(&kev, fd, EVFILT_VNODE, EV_ADD|EV_ENABLE|EV_CLEAR,
535 NOTE_WRITE, 0, opaque);
536 if (kevent(kq, &kev, 1, NULL, 0, NULL) == -1) {
537 *error = errno;
538 return -1;
539 }
540
541 return kq;
542 }
543
544 int
545 rumpuser_writewatchfile_wait(int kq, intptr_t *opaque, int *error)
546 {
547 struct kevent kev;
548 int rv;
549
550 KLOCK_WRAP(rv = kevent(kq, NULL, 0, &kev, 1, NULL));
551 if (rv == -1) {
552 *error = errno;
553 return -1;
554 }
555
556 if (opaque)
557 *opaque = kev.udata;
558 return rv;
559 }
560
561 /*
562 * This is meant for safe debugging prints from the kernel.
563 */
564 int
565 rumpuser_dprintf(const char *format, ...)
566 {
567 va_list ap;
568 int rv;
569
570 va_start(ap, format);
571 rv = vprintf(format, ap);
572 va_end(ap);
573
574 return rv;
575 }
576
577 int
578 rumpuser_kill(int64_t pid, int sig, int *error)
579 {
580
581 #ifdef __NetBSD__
582 if (pid == RUMPUSER_PID_SELF) {
583 DOCALL(int, raise(sig));
584 } else {
585 DOCALL(int, kill((pid_t)pid, sig));
586 }
587 #else
588 /* XXXfixme: signal numbers may not match on non-NetBSD */
589 *error = EOPNOTSUPP;
590 return -1;
591 #endif
592 }
593
594 int
595 rumpuser_getnhostcpu(void)
596 {
597 int ncpu;
598 size_t sz = sizeof(ncpu);
599
600 #ifdef __NetBSD__
601 if (sysctlbyname("hw.ncpu", &ncpu, &sz, NULL, 0) == -1)
602 return 1;
603 return ncpu;
604 #else
605 return 1;
606 #endif
607 }
608