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