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