Module Name:    src
Committed By:   pooka
Date:           Fri Feb 26 18:54:21 UTC 2010

Modified Files:
        src/lib/librumpuser: Makefile
        src/sys/rump/librump: Makefile
Added Files:
        src/lib/librumpuser: rumpuser.3 rumpuser.c rumpuser_dl.c rumpuser_int.h
            rumpuser_net.c rumpuser_pth.c rumpuser_pth_dummy.c shlib_version
Removed Files:
        src/sys/rump/librump/rumpuser: Makefile rumpuser.3 rumpuser.c
            rumpuser_dl.c rumpuser_int.h rumpuser_net.c rumpuser_pth.c
            rumpuser_pth_dummy.c

Log Message:
librumpuser is not _KERNEL code and does not belong in sys.  Move
the source files from src/sys/rump/librump/rumpuser to src/lib/librumpuser
(from where it is already built).  Even so, keep rumpuser.h in
sys/rump/include for kernel source tree self-containment.


To generate a diff of this commit:
cvs rdiff -u -r1.1 -r1.2 src/lib/librumpuser/Makefile
cvs rdiff -u -r0 -r1.1 src/lib/librumpuser/rumpuser.3 \
    src/lib/librumpuser/rumpuser.c src/lib/librumpuser/rumpuser_dl.c \
    src/lib/librumpuser/rumpuser_int.h src/lib/librumpuser/rumpuser_net.c \
    src/lib/librumpuser/rumpuser_pth.c \
    src/lib/librumpuser/rumpuser_pth_dummy.c \
    src/lib/librumpuser/shlib_version
cvs rdiff -u -r1.7 -r1.8 src/sys/rump/librump/Makefile
cvs rdiff -u -r1.16 -r0 src/sys/rump/librump/rumpuser/Makefile
cvs rdiff -u -r1.1 -r0 src/sys/rump/librump/rumpuser/rumpuser.3
cvs rdiff -u -r1.49 -r0 src/sys/rump/librump/rumpuser/rumpuser.c
cvs rdiff -u -r1.12 -r0 src/sys/rump/librump/rumpuser/rumpuser_dl.c
cvs rdiff -u -r1.4 -r0 src/sys/rump/librump/rumpuser/rumpuser_int.h
cvs rdiff -u -r1.7 -r0 src/sys/rump/librump/rumpuser/rumpuser_net.c
cvs rdiff -u -r1.40 -r0 src/sys/rump/librump/rumpuser/rumpuser_pth.c
cvs rdiff -u -r1.8 -r0 src/sys/rump/librump/rumpuser/rumpuser_pth_dummy.c

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/lib/librumpuser/Makefile
diff -u src/lib/librumpuser/Makefile:1.1 src/lib/librumpuser/Makefile:1.2
--- src/lib/librumpuser/Makefile:1.1	Tue Jul 29 13:17:41 2008
+++ src/lib/librumpuser/Makefile	Fri Feb 26 18:54:20 2010
@@ -1,9 +1,24 @@
-#	$NetBSD: Makefile,v 1.1 2008/07/29 13:17:41 pooka Exp $
+#	$NetBSD: Makefile,v 1.2 2010/02/26 18:54:20 pooka Exp $
 #
 
-.PATH:		${.CURDIR}/../../sys/rump/librump/rumpuser
+WARNS=		4
 
+# rumpuser.h is in sys/rump for inclusion by kernel components
+.PATH:		${.CURDIR}/../../sys/rump/include/rump
+
+LIB=		rumpuser
 LIBDPLIBS+=     pthread ${.CURDIR}/../libpthread
+
+SRCS=		rumpuser.c rumpuser_net.c
+SRCS+=		rumpuser_pth.c
+SRCS+=		rumpuser_dl.c
+
+INCSDIR=	/usr/include/rump
+INCS=		rumpuser.h
+
 MAN=		rumpuser.3
 
-.include "${.CURDIR}/../../sys/rump/librump/rumpuser/Makefile"
+CPPFLAGS+=	-D_REENTRANT
+
+
+.include <bsd.lib.mk>

Index: src/sys/rump/librump/Makefile
diff -u src/sys/rump/librump/Makefile:1.7 src/sys/rump/librump/Makefile:1.8
--- src/sys/rump/librump/Makefile:1.7	Tue Nov 17 15:36:28 2009
+++ src/sys/rump/librump/Makefile	Fri Feb 26 18:54:20 2010
@@ -1,11 +1,7 @@
-#	$NetBSD: Makefile,v 1.7 2009/11/17 15:36:28 pooka Exp $
+#	$NetBSD: Makefile,v 1.8 2010/02/26 18:54:20 pooka Exp $
 #
 
 SUBDIR= 	rumpkern
-.if !defined(NORUMPUSER)
-SUBDIR+=	rumpuser
-.endif
-
 SUBDIR+=	rumpcrypto rumpdev rumpnet rumpvfs
 
 .include <bsd.subdir.mk>

Added files:

Index: src/lib/librumpuser/rumpuser.3
diff -u /dev/null src/lib/librumpuser/rumpuser.3:1.1
--- /dev/null	Fri Feb 26 18:54:21 2010
+++ src/lib/librumpuser/rumpuser.3	Fri Feb 26 18:54:20 2010
@@ -0,0 +1,50 @@
+.\"     $NetBSD: rumpuser.3,v 1.1 2010/02/26 18:54:20 pooka Exp $
+.\"
+.\" Copyright (c) 2008 Antti Kantee.  All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\"    notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\"    notice, this list of conditions and the following disclaimer in the
+.\"    documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.Dd July 28, 2008
+.Dt RUMPUSER 3
+.Os
+.Sh NAME
+.Nm rumpuser
+.Nd rump user namespace helper routines
+.Sh LIBRARY
+rump User Library (librumpuser, \-lrumpuser)
+.Sh SYNOPSIS
+.In rump/rumpuser.h
+.Sh DESCRIPTION
+.Nm
+is a user namespace helper library for runnable userspace meta programs.
+From rump emulation code compiled with
+.Dv -D_KERNEL
+is it not possible to directly call routines such as system calls
+for reading and writing files or devices.
+This is where
+.Nm
+comes into play by providing a bridge between the two namespaces.
+Another example of its services is a way to call pthread routines,
+used for emulating kernel threads and synchronization mechanisms
+in rump.
+.Sh SEE ALSO
+.Xr rump 3
Index: src/lib/librumpuser/rumpuser.c
diff -u /dev/null src/lib/librumpuser/rumpuser.c:1.1
--- /dev/null	Fri Feb 26 18:54:21 2010
+++ src/lib/librumpuser/rumpuser.c	Fri Feb 26 18:54:20 2010
@@ -0,0 +1,573 @@
+/*	$NetBSD: rumpuser.c,v 1.1 2010/02/26 18:54:20 pooka Exp $	*/
+
+/*
+ * Copyright (c) 2007-2010 Antti Kantee.  All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#if !defined(lint)
+__RCSID("$NetBSD: rumpuser.c,v 1.1 2010/02/26 18:54:20 pooka Exp $");
+#endif /* !lint */
+
+/* thank the maker for this */
+#ifdef __linux__
+#define _XOPEN_SOURCE 500
+#define _BSD_SOURCE
+#define _FILE_OFFSET_BITS 64
+#include <features.h>
+#endif
+
+#include <sys/param.h>
+#include <sys/event.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/uio.h>
+
+#ifdef __NetBSD__
+#include <sys/disklabel.h>
+#endif
+
+#include <assert.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <poll.h>
+#include <stdarg.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <rump/rumpuser.h>
+
+#include "rumpuser_int.h"
+
+int
+rumpuser_getfileinfo(const char *path, uint64_t *sizep, int *ftp, int *error)
+{
+	struct stat sb;
+	uint64_t size;
+	int needsdev = 0, rv = 0, ft;
+
+	if (stat(path, &sb) == -1) {
+		*error = errno;
+		return -1;
+	}
+
+	switch (sb.st_mode & S_IFMT) {
+	case S_IFDIR:
+		ft = RUMPUSER_FT_DIR;
+		break;
+	case S_IFREG:
+		ft = RUMPUSER_FT_REG;
+		break;
+	case S_IFBLK:
+		ft = RUMPUSER_FT_BLK;
+		needsdev = 1;
+		break;
+	case S_IFCHR:
+		ft = RUMPUSER_FT_CHR;
+		needsdev = 1;
+		break;
+	default:
+		ft = RUMPUSER_FT_OTHER;
+		break;
+	}
+
+	if (!needsdev) {
+		size = sb.st_size;
+	} else if (sizep) {
+		/*
+		 * Welcome to the jungle.  Of course querying the kernel
+		 * for a device partition size is supposed to be far from
+		 * trivial.  On NetBSD we use ioctl.  On $other platform
+		 * we have a problem.  We try "the lseek trick" and just
+		 * fail if that fails.  Platform specific code can later
+		 * be written here if appropriate.
+		 *
+		 * On NetBSD we hope and pray that for block devices nobody
+		 * else is holding them open, because otherwise the kernel
+		 * will not permit us to open it.  Thankfully, this is
+		 * usually called only in bootstrap and then we can
+		 * forget about it.
+		 */
+#ifndef __NetBSD__
+		off_t off;
+		int fd;
+
+		fd = open(path, O_RDONLY);
+		if (fd == -1) {
+			*error = errno;
+			rv = -1;
+			goto out;
+		}
+
+		off = lseek(fd, 0, SEEK_END);
+		close(fd);
+		if (off != 0) {
+			size = off;
+			goto out;
+		}
+		fprintf(stderr, "error: device size query not implemented on "
+		    "this platform\n");
+		*error = EOPNOTSUPP;
+		rv = -1;
+		goto out;
+#else
+		struct disklabel lab;
+		struct partition *parta;
+		int fd;
+
+		fd = open(path, O_RDONLY);
+		if (fd == -1) {
+			*error = errno;
+			rv = -1;
+			goto out;
+		}
+
+		if (ioctl(fd, DIOCGDINFO, &lab) == -1) {
+			*error = errno;
+			rv = -1;
+			goto out;
+		}
+		close(fd);
+
+		parta = &lab.d_partitions[DISKPART(sb.st_rdev)];
+		size = (uint64_t)lab.d_secsize * parta->p_size;
+#endif /* __NetBSD__ */
+	}
+
+ out:
+	if (rv == 0 && sizep)
+		*sizep = size;
+	if (rv == 0 && ftp)
+		*ftp = ft;
+
+	return rv;
+}
+
+int
+rumpuser_nanosleep(uint64_t *sec, uint64_t *nsec, int *error)
+{
+	struct timespec rqt, rmt;
+	int rv;
+
+	/*LINTED*/
+	rqt.tv_sec = *sec;
+	/*LINTED*/
+	rqt.tv_nsec = *nsec;
+
+	KLOCK_WRAP(rv = nanosleep(&rqt, &rmt));
+	if (rv == -1)
+		*error = errno;
+
+	*sec = rmt.tv_sec;
+	*nsec = rmt.tv_nsec;
+
+	return rv;
+}
+
+void *
+rumpuser__malloc(size_t howmuch, int canfail, const char *func, int line)
+{
+	void *rv;
+
+	rv = malloc(howmuch);
+	if (rv == NULL && canfail == 0) {
+		warn("malloc failed %s (%d)", func, line);
+		abort();
+	}
+
+	return rv;
+}
+
+void *
+rumpuser__realloc(void *ptr, size_t howmuch, int canfail,
+	const char *func, int line)
+{
+	void *rv;
+
+	rv = realloc(ptr, howmuch);
+	if (rv == NULL && canfail == 0) {
+		warn("realloc failed %s (%d)", func, line);
+		abort();
+	}
+
+	return rv;
+}
+
+void
+rumpuser_free(void *ptr)
+{
+
+	free(ptr);
+}
+
+void *
+rumpuser_anonmmap(size_t size, int alignbit, int exec, int *error)
+{
+	void *rv;
+	int prot;
+
+	prot = PROT_READ|PROT_WRITE;
+	if (exec)
+		prot |= PROT_EXEC;
+	/* XXX: MAP_ALIGNED() is not portable */
+	rv = mmap(NULL, size, prot, MAP_ANON | MAP_ALIGNED(alignbit), -1, 0);
+	if (rv == MAP_FAILED) {
+		*error = errno;
+		return NULL;
+	}
+	return rv;
+}
+
+void
+rumpuser_unmap(void *addr, size_t len)
+{
+	int rv;
+
+	rv = munmap(addr, len);
+	assert(rv == 0);
+}
+
+void *
+rumpuser_filemmap(int fd, off_t offset, size_t len, int flags, int *error)
+{
+	void *rv;
+	int mmflags, prot;
+
+	if (flags & RUMPUSER_FILEMMAP_TRUNCATE)
+		ftruncate(fd, offset + len);
+
+	mmflags = MAP_FILE;
+	if (flags & RUMPUSER_FILEMMAP_SHARED)
+		mmflags |= MAP_SHARED;
+	else
+		mmflags |= MAP_PRIVATE;
+
+	prot = 0;
+	if (flags & RUMPUSER_FILEMMAP_READ)
+		prot |= PROT_READ;
+	if (flags & RUMPUSER_FILEMMAP_WRITE)
+		prot |= PROT_WRITE;
+
+	rv = mmap(NULL, len, PROT_READ|PROT_WRITE, mmflags, fd, offset);
+	if (rv == MAP_FAILED) {
+		*error = errno;
+		return NULL;
+	}
+
+	*error = 0;
+	return rv;
+}
+
+int
+rumpuser_memsync(void *addr, size_t len, int *error)
+{
+
+	DOCALL_KLOCK(int, (msync(addr, len, MS_SYNC)));
+}
+
+int
+rumpuser_open(const char *path, int flags, int *error)
+{
+
+	DOCALL(int, (open(path, flags, 0644)));
+}
+
+int
+rumpuser_ioctl(int fd, u_long cmd, void *data, int *error)
+{
+
+	DOCALL_KLOCK(int, (ioctl(fd, cmd, data)));
+}
+
+int
+rumpuser_close(int fd, int *error)
+{
+
+	DOCALL(int, close(fd));
+}
+
+int
+rumpuser_fsync(int fd, int *error)
+{
+
+	DOCALL_KLOCK(int, fsync(fd));
+}
+
+ssize_t
+rumpuser_read(int fd, void *data, size_t size, int *error)
+{
+	ssize_t rv;
+
+	KLOCK_WRAP(rv = read(fd, data, size));
+	if (rv == -1)
+		*error = errno;
+
+	return rv;
+}
+
+ssize_t
+rumpuser_pread(int fd, void *data, size_t size, off_t offset, int *error)
+{
+	ssize_t rv;
+
+	KLOCK_WRAP(rv = pread(fd, data, size, offset));
+	if (rv == -1)
+		*error = errno;
+
+	return rv;
+}
+
+void
+rumpuser_read_bio(int fd, void *data, size_t size, off_t offset,
+	rump_biodone_fn biodone, void *biodonecookie)
+{
+	ssize_t rv;
+	int error = 0;
+
+	rv = rumpuser_pread(fd, data, size, offset, &error);
+	/* check against <0 instead of ==-1 to get typing below right */
+	if (rv < 0)
+		rv = 0;
+
+	/* LINTED: see above */
+	biodone(biodonecookie, rv, error);
+}
+
+ssize_t
+rumpuser_write(int fd, const void *data, size_t size, int *error)
+{
+	ssize_t rv;
+
+	KLOCK_WRAP(rv = write(fd, data, size));
+	if (rv == -1)
+		*error = errno;
+
+	return rv;
+}
+
+ssize_t
+rumpuser_pwrite(int fd, const void *data, size_t size, off_t offset, int *error)
+{
+	ssize_t rv;
+
+	KLOCK_WRAP(rv = pwrite(fd, data, size, offset));
+	if (rv == -1)
+		*error = errno;
+
+	return rv;
+}
+
+void
+rumpuser_write_bio(int fd, const void *data, size_t size, off_t offset,
+	rump_biodone_fn biodone, void *biodonecookie)
+{
+	ssize_t rv;
+	int error = 0;
+
+	rv = rumpuser_pwrite(fd, data, size, offset, &error);
+	/* check against <0 instead of ==-1 to get typing below right */
+	if (rv < 0)
+		rv = 0;
+
+	/* LINTED: see above */
+	biodone(biodonecookie, rv, error);
+}
+
+ssize_t
+rumpuser_readv(int fd, const struct rumpuser_iovec *riov, int iovcnt,
+	int *error)
+{
+	struct iovec *iovp;
+	ssize_t rv;
+	int i;
+
+	iovp = malloc(iovcnt * sizeof(struct iovec));
+	if (iovp == NULL) {
+		*error = ENOMEM;
+		return -1;
+	}
+	for (i = 0; i < iovcnt; i++) {
+		iovp[i].iov_base = riov[i].iov_base;
+		/*LINTED*/
+		iovp[i].iov_len = riov[i].iov_len;
+	}
+
+	KLOCK_WRAP(rv = readv(fd, iovp, iovcnt));
+	if (rv == -1)
+		*error = errno;
+	free(iovp);
+
+	return rv;
+}
+
+ssize_t
+rumpuser_writev(int fd, const struct rumpuser_iovec *riov, int iovcnt,
+	int *error)
+{
+	struct iovec *iovp;
+	ssize_t rv;
+	int i;
+
+	iovp = malloc(iovcnt * sizeof(struct iovec));
+	if (iovp == NULL) {
+		*error = ENOMEM;
+		return -1;
+	}
+	for (i = 0; i < iovcnt; i++) {
+		iovp[i].iov_base = riov[i].iov_base;
+		/*LINTED*/
+		iovp[i].iov_len = riov[i].iov_len;
+	}
+
+	KLOCK_WRAP(rv = writev(fd, iovp, iovcnt));
+	if (rv == -1)
+		*error = errno;
+	free(iovp);
+
+	return rv;
+}
+
+int
+rumpuser_gettime(uint64_t *sec, uint64_t *nsec, int *error)
+{
+	struct timeval tv;
+	int rv;
+
+	rv = gettimeofday(&tv, NULL);
+	if (rv == -1) {
+		*error = errno;
+		return rv;
+	}
+
+	*sec = tv.tv_sec;
+	*nsec = tv.tv_usec * 1000;
+
+	return 0;
+}
+
+int
+rumpuser_getenv(const char *name, char *buf, size_t blen, int *error)
+{
+
+	DOCALL(int, getenv_r(name, buf, blen));
+}
+
+int
+rumpuser_gethostname(char *name, size_t namelen, int *error)
+{
+
+	DOCALL(int, (gethostname(name, namelen)));
+}
+
+int
+rumpuser_poll(struct pollfd *fds, int nfds, int timeout, int *error)
+{
+
+	DOCALL_KLOCK(int, (poll(fds, (nfds_t)nfds, timeout)));
+}
+
+int
+rumpuser_putchar(int c, int *error)
+{
+
+	DOCALL(int, (putchar(c)));
+}
+
+void
+rumpuser_exit(int rv)
+{
+
+	if (rv == RUMPUSER_PANIC)
+		abort();
+	else
+		exit(rv);
+}
+
+void
+rumpuser_seterrno(int error)
+{
+
+	errno = error;
+}
+
+int
+rumpuser_writewatchfile_setup(int kq, int fd, intptr_t opaque, int *error)
+{
+	struct kevent kev;
+
+	if (kq == -1) {
+		kq = kqueue();
+		if (kq == -1) {
+			*error = errno;
+			return -1;
+		}
+	}
+
+	EV_SET(&kev, fd, EVFILT_VNODE, EV_ADD|EV_ENABLE|EV_CLEAR,
+	    NOTE_WRITE, 0, opaque);
+	if (kevent(kq, &kev, 1, NULL, 0, NULL) == -1) {
+		*error = errno;
+		return -1;
+	}
+
+	return kq;
+}
+
+int
+rumpuser_writewatchfile_wait(int kq, intptr_t *opaque, int *error)
+{
+	struct kevent kev;
+	int rv;
+
+	KLOCK_WRAP(rv = kevent(kq, NULL, 0, &kev, 1, NULL));
+	if (rv == -1) {
+		*error = errno;
+		return -1;
+	}
+
+	if (opaque)
+		*opaque = kev.udata;
+	return rv;
+}
+
+/*
+ * This is meant for safe debugging prints from the kernel.
+ */
+int
+rumpuser_dprintf(const char *format, ...)
+{
+	va_list ap;
+	int rv;
+
+	va_start(ap, format);
+	rv = vprintf(format, ap);
+	va_end(ap);
+
+	return rv;
+}
Index: src/lib/librumpuser/rumpuser_dl.c
diff -u /dev/null src/lib/librumpuser/rumpuser_dl.c:1.1
--- /dev/null	Fri Feb 26 18:54:21 2010
+++ src/lib/librumpuser/rumpuser_dl.c	Fri Feb 26 18:54:20 2010
@@ -0,0 +1,413 @@
+/*      $NetBSD: rumpuser_dl.c,v 1.1 2010/02/26 18:54:20 pooka Exp $	*/
+
+/*
+ * Copyright (c) 2009 Antti Kantee.  All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Load all module link sets and feed symbol table to the kernel.
+ * Called during rump bootstrap.
+ */
+
+#include <sys/cdefs.h>
+__RCSID("$NetBSD: rumpuser_dl.c,v 1.1 2010/02/26 18:54:20 pooka Exp $");
+
+#include <sys/types.h>
+#include <sys/time.h>
+
+#include <assert.h>
+#include <dlfcn.h>
+#include <elf.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <link.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <rump/rumpuser.h>
+
+#if defined(__ELF__) && (defined(__NetBSD__) || defined(__FreeBSD__)	\
+    || (defined(__sun__) && defined(__svr4__)))
+static size_t symtabsize = 0, strtabsize = 0;
+static size_t symtaboff = 0, strtaboff = 0;
+static uint8_t *symtab = NULL;
+static char *strtab = NULL;
+static unsigned char eident;
+
+static void *
+reservespace(void *store, size_t *storesize,
+	size_t storeoff, size_t required)
+{
+	size_t chunk, newsize;
+
+	assert(storeoff <= *storesize);
+	chunk = *storesize - storeoff;
+
+	if (chunk >= required)
+		return store;
+
+	newsize = *storesize + ((size_t)required - chunk);
+	store = realloc(store, newsize);
+	if (store == NULL) {
+		return NULL;
+	}
+	*((uint8_t *)store + storeoff) = '\0';
+	*storesize = newsize;
+
+	return store;
+}
+
+/*
+ * Macros to make handling elf32/64 in the code a little saner.
+ */
+
+#define EHDR_GETMEMBER(base, thevar, result)				\
+do {									\
+	if (eident == ELFCLASS32) {					\
+		Elf32_Ehdr *ehdr = base;				\
+		/*LINTED*/						\
+		result = ehdr->thevar;					\
+	} else {							\
+		Elf64_Ehdr *ehdr = base;				\
+		/*LINTED*/						\
+		result = ehdr->thevar;					\
+	}								\
+} while (/*CONSTCOND*/0)
+
+#define SHDRn_GETMEMBER(base, n, thevar, result)			\
+do {									\
+	if (eident == ELFCLASS32) {					\
+		Elf32_Shdr *shdr = base;				\
+		/*LINTED*/						\
+		result = shdr[n].thevar;				\
+	} else {							\
+		Elf64_Shdr *shdr = base;				\
+		/*LINTED*/						\
+		result = shdr[n].thevar;				\
+	}								\
+} while (/*CONSTCOND*/0)
+
+#define DYNn_GETMEMBER(base, n, thevar, result)				\
+do {									\
+	if (eident == ELFCLASS32) {					\
+		Elf32_Dyn *dyn = base;					\
+		/*LINTED*/						\
+		result = dyn[n].thevar;					\
+	} else {							\
+		Elf64_Dyn *dyn = base;					\
+		/*LINTED*/						\
+		result = dyn[n].thevar;					\
+	}								\
+} while (/*CONSTCOND*/0)
+
+#define SYMn_GETMEMBER(base, n, thevar, result)				\
+do {									\
+	if (eident == ELFCLASS32) {					\
+		Elf32_Sym *sym = base;					\
+		/*LINTED*/						\
+		result = sym[n].thevar;					\
+	} else {							\
+		Elf64_Sym *sym = base;					\
+		/*LINTED*/						\
+		result = sym[n].thevar;					\
+	}								\
+} while (/*CONSTCOND*/0)
+
+#define SYMn_SETMEMBER(base, n, thevar, value)				\
+do {									\
+	if (eident == ELFCLASS32) {					\
+		Elf32_Sym *sym = base;					\
+		/*LINTED*/						\
+		sym[n].thevar = value;					\
+	} else {							\
+		Elf64_Sym *sym = base;					\
+		/*LINTED*/						\
+		sym[n].thevar = value;					\
+	}								\
+} while (/*CONSTCOND*/0)
+
+#define SYM_GETSIZE() ((eident==ELFCLASS32)?sizeof(Elf32_Sym):sizeof(Elf64_Sym))
+
+static int
+getsymbols(struct link_map *map)
+{
+	void *libbase = map->l_addr;
+	int i = 0, fd;
+	char *str_base;
+	void *syms_base = NULL; /* XXXgcc */
+	size_t cursymsize, curstrsize;
+	void *shdr_base;
+	size_t shsize;
+	int shnum;
+	uint64_t shoff;
+	void *ed_base;
+	uint64_t ed_tag;
+	int sverrno;
+
+	if (memcmp(libbase, ELFMAG, SELFMAG) != 0)
+		return ENOEXEC;
+	eident = *(unsigned char *)(map->l_addr + EI_CLASS);
+	if (eident != ELFCLASS32 && eident != ELFCLASS64)
+		return ENOEXEC;
+
+	/* read the section headers from disk to determine size of dynsym */
+	fd = open(map->l_name, O_RDONLY);
+	if (fd == -1) {
+		sverrno = errno;
+		fprintf(stderr, "open %s failed\n", map->l_name);
+		return sverrno;
+	}
+
+	EHDR_GETMEMBER(libbase, e_shnum, shnum);
+	EHDR_GETMEMBER(libbase, e_shentsize, shsize);
+	EHDR_GETMEMBER(libbase, e_shoff, shoff);
+	shdr_base = malloc(shnum * shsize);
+	if (pread(fd, shdr_base, shnum * shsize, (off_t)shoff)
+	    != (ssize_t)(shnum*shsize)){
+		sverrno = errno;
+		fprintf(stderr, "read section headers for %s failed\n",
+		    map->l_name);
+		free(shdr_base);
+		close(fd);
+		return sverrno;
+	}
+	cursymsize = (size_t)-1;
+	for (i = 1; i <= shnum; i++) {
+		int shtype;
+
+		SHDRn_GETMEMBER(shdr_base, i, sh_type, shtype);
+		if (shtype != SHT_DYNSYM)
+			continue;
+		SHDRn_GETMEMBER(shdr_base, i, sh_size, cursymsize);
+		break;
+	}
+	free(shdr_base);
+	close(fd);
+	if (cursymsize == (size_t)-1) {
+		fprintf(stderr, "could not find dynsym size from %s\n",
+		    map->l_name);
+		return ENOEXEC;
+	}
+
+	/* find symtab, strtab and strtab size */
+	str_base = NULL;
+	curstrsize = (size_t)-1;
+	ed_base = map->l_ld;
+	i = 0;
+	DYNn_GETMEMBER(ed_base, i, d_tag, ed_tag);
+	while (ed_tag != DT_NULL) {
+		uintptr_t edptr;
+		size_t edval;
+
+		switch (ed_tag) {
+		case DT_SYMTAB:
+			DYNn_GETMEMBER(ed_base, i, d_un.d_ptr, edptr);
+			syms_base = map->l_addr + edptr;
+			break;
+		case DT_STRTAB:
+			DYNn_GETMEMBER(ed_base, i, d_un.d_ptr, edptr);
+			str_base = map->l_addr + edptr;
+			break;
+		case DT_STRSZ:
+			DYNn_GETMEMBER(ed_base, i, d_un.d_val, edval);
+			curstrsize = edval;
+			break;
+		default:
+			break;
+		}
+		i++;
+		DYNn_GETMEMBER(ed_base, i, d_tag, ed_tag);
+	} while (ed_tag != DT_NULL);
+
+	if (str_base == NULL || syms_base == NULL || curstrsize == (size_t)-1) {
+		fprintf(stderr, "could not find strtab, symtab or strtab size "
+		    "in %s\n", map->l_name);
+		return ENOEXEC;
+	}
+
+	/*
+	 * Make sure we have enough space for the contents of the symbol
+	 * and string tables we are currently processing.  The total used
+	 * space will be smaller due to undefined symbols we are not
+	 * interested in.
+	 */
+	symtab = reservespace(symtab, &symtabsize, symtaboff, cursymsize);
+	strtab = reservespace(strtab, &strtabsize, strtaboff, curstrsize);
+	if (symtab == NULL || strtab == NULL) {
+		fprintf(stderr, "failed to reserve memory");
+		return ENOMEM;
+	}
+
+	/* iterate over all symbols in current symtab */
+	for (i = 0; i * SYM_GETSIZE() < cursymsize; i++) {
+		char *cursymname;
+		int shndx, name;
+		uintptr_t value;
+		void *csym;
+
+		SYMn_GETMEMBER(syms_base, i, st_shndx, shndx);
+		SYMn_GETMEMBER(syms_base, i, st_value, value);
+		if (shndx == SHN_UNDEF || value == 0)
+			continue;
+
+		/* get symbol name */
+		SYMn_GETMEMBER(syms_base, i, st_name, name);
+		cursymname = name + str_base;
+		memcpy(symtab + symtaboff,
+		    (uint8_t *)syms_base + i*SYM_GETSIZE(), SYM_GETSIZE());
+
+		/*
+		 * set name to point at new strtab, offset symbol value
+		 * with lib base address.
+		 */
+		csym = symtab + symtaboff;
+		SYMn_SETMEMBER(csym, 0, st_name, strtaboff);
+		SYMn_GETMEMBER(csym, 0, st_value, value);
+		SYMn_SETMEMBER(csym, 0, st_value,(intptr_t)(value+map->l_addr));
+		symtaboff += SYM_GETSIZE();
+
+		strcpy(strtab + strtaboff, cursymname);
+		strtaboff += strlen(cursymname)+1;
+	}
+
+	return 0;
+}
+
+static int
+process(const char *soname, rump_modinit_fn domodinit)
+{
+	void *handle;
+	struct modinfo **mi, **mi_end;
+	int loaded = 0;
+
+	if (strstr(soname, "librump") == NULL)
+		return 0;
+
+	handle = dlopen(soname, RTLD_LAZY);
+	if (handle == NULL)
+		return 0;
+
+	mi = dlsym(handle, "__start_link_set_modules");
+	if (!mi)
+		goto out;
+	mi_end = dlsym(handle, "__stop_link_set_modules");
+	if (!mi_end)
+		goto out;
+
+	for (; mi < mi_end; mi++)
+		if (domodinit(*mi, NULL) == 0)
+			loaded = 1;
+	assert(mi == mi_end);
+
+ out:
+	dlclose(handle);
+	return loaded;
+}
+
+/*
+ * Get the linkmap from the dynlinker.  Try to load kernel modules
+ * from all objects in the linkmap.
+ */
+void
+rumpuser_dl_bootstrap(rump_modinit_fn domodinit,
+	rump_symload_fn symload)
+{
+	struct link_map *map, *origmap;
+	int couldload;
+	int error;
+
+	if (dlinfo(RTLD_SELF, RTLD_DI_LINKMAP, &origmap) == -1) {
+		fprintf(stderr, "warning: rumpuser module bootstrap "
+		    "failed: %s\n", dlerror());
+		return;
+	}
+	/*
+	 * Process last->first because that's the most probable
+	 * order for dependencies
+	 */
+	for (; origmap->l_next; origmap = origmap->l_next)
+		continue;
+
+	/*
+	 * Build symbol table to hand to the rump kernel.  Do this by
+	 * iterating over all rump libraries and collecting symbol
+	 * addresses and relocation info.
+	 */
+	error = 0;
+	for (map = origmap; map && !error; map = map->l_prev) {
+		if (map->l_addr == NULL)
+			continue;
+		if (strstr(map->l_name, "librump") != NULL)
+			error = getsymbols(map);
+	}
+
+	if (error == 0) {
+		void *trimmedsym, *trimmedstr;
+
+		/*
+		 * Allocate optimum-sized memory for storing tables
+		 * and feed to kernel.  If memory allocation fails,
+		 * just give the ones with extra context (although
+		 * I'm pretty sure we'll die moments later due to
+		 * memory running out).
+		 */
+		if ((trimmedsym = malloc(symtaboff)) != NULL) {
+			memcpy(trimmedsym, symtab, symtaboff);
+		} else {
+			trimmedsym = symtab;
+			symtab = NULL;
+		}
+		if ((trimmedstr = malloc(strtaboff)) != NULL) {
+			memcpy(trimmedstr, strtab, strtaboff);
+		} else {
+			trimmedstr = strtab;
+			strtab = NULL;
+		}
+		symload(trimmedsym, symtaboff, trimmedstr, strtaboff);
+	}
+	free(symtab);
+	free(strtab);
+
+	/*
+	 * Next, load modules from dynlibs.
+	 */
+	do {
+		couldload = 0;
+		map = origmap;
+		for (; map; map = map->l_prev)
+			if (process(map->l_name, domodinit))
+				couldload = 1;
+	} while (couldload);
+}
+#else
+void
+rumpuser_dl_bootstrap(rump_modinit_fn domodinit,
+	rump_symload_fn symload)
+{
+
+	fprintf(stderr, "Warning, dlinfo() unsupported on host?\n");
+}
+#endif
Index: src/lib/librumpuser/rumpuser_int.h
diff -u /dev/null src/lib/librumpuser/rumpuser_int.h:1.1
--- /dev/null	Fri Feb 26 18:54:21 2010
+++ src/lib/librumpuser/rumpuser_int.h	Fri Feb 26 18:54:20 2010
@@ -0,0 +1,61 @@
+/*	$NetBSD: rumpuser_int.h,v 1.1 2010/02/26 18:54:20 pooka Exp $	*/
+
+/*
+ * Copyright (c) 2008 Antti Kantee.  All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <rump/rumpuser.h>
+
+extern kernel_lockfn rumpuser__klock;
+extern kernel_unlockfn rumpuser__kunlock;
+extern int rumpuser__wantthreads;
+
+#define KLOCK_WRAP(a)							\
+do {									\
+	int nlocks;							\
+	rumpuser__kunlock(0, &nlocks);					\
+	a;								\
+	rumpuser__klock(nlocks);					\
+} while (/*CONSTCOND*/0)
+
+#define DOCALL(rvtype, call)						\
+	rvtype rv;							\
+	rv = call;							\
+	if (rv == -1)							\
+		*error = errno;						\
+	else								\
+		*error = 0;						\
+	return rv;
+
+#define DOCALL_KLOCK(rvtype, call)					\
+	rvtype rv;							\
+	int nlocks;							\
+	rumpuser__kunlock(0, &nlocks);					\
+	rv = call;							\
+	rumpuser__klock(nlocks);					\
+	if (rv == -1)							\
+		*error = errno;						\
+	else								\
+		*error = 0;						\
+	return rv;
Index: src/lib/librumpuser/rumpuser_net.c
diff -u /dev/null src/lib/librumpuser/rumpuser_net.c:1.1
--- /dev/null	Fri Feb 26 18:54:21 2010
+++ src/lib/librumpuser/rumpuser_net.c	Fri Feb 26 18:54:20 2010
@@ -0,0 +1,125 @@
+/*	$NetBSD: rumpuser_net.c,v 1.1 2010/02/26 18:54:20 pooka Exp $	*/
+
+/*
+ * Copyright (c) 2008 Antti Kantee.  All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#if !defined(lint)
+__RCSID("$NetBSD: rumpuser_net.c,v 1.1 2010/02/26 18:54:20 pooka Exp $");
+#endif /* !lint */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <errno.h>
+
+#include <rump/rumpuser.h>
+
+#include "rumpuser_int.h"
+
+int
+rumpuser_net_socket(int domain, int type, int proto, int *error)
+{
+
+	DOCALL_KLOCK(int, (socket(domain, type, proto)));
+}
+
+int
+rumpuser_net_sendmsg(int s, const struct msghdr *msg, int flags, int *error)
+{
+
+	DOCALL_KLOCK(ssize_t, (sendmsg(s, msg, flags)));
+}
+
+int
+rumpuser_net_recvmsg(int s, struct msghdr *msg, int flags, int *error)
+{
+
+	DOCALL_KLOCK(ssize_t, (recvmsg(s, msg, flags)));
+}
+
+int
+rumpuser_net_connect(int s, const struct sockaddr *name, int len, int *error)
+{
+
+	DOCALL_KLOCK(int, (connect(s, name, (socklen_t)len)));
+}
+
+int
+rumpuser_net_bind(int s, const struct sockaddr *name, int len, int *error)
+{
+
+	DOCALL_KLOCK(int, (bind(s, name, (socklen_t)len)));
+}
+
+int
+rumpuser_net_accept(int s, struct sockaddr *name, int *lenp, int *error)
+{
+
+	DOCALL_KLOCK(int, (accept(s, name, (socklen_t *)lenp)));
+}
+
+int
+rumpuser_net_listen(int s, int backlog, int *error)
+{
+
+	DOCALL_KLOCK(int, (listen(s, backlog)));
+}
+
+int
+rumpuser_net_getname(int s, struct sockaddr *so, int *lenp,
+	enum rumpuser_getnametype which, int *error)
+{
+	socklen_t slen = *lenp;
+	int rv;
+
+	if (which == RUMPUSER_SOCKNAME)
+		rv = getsockname(s, so, &slen);
+	else
+		rv = getpeername(s, so, &slen);
+
+	*lenp = slen;
+	if (rv == -1)
+		*error = errno;
+	else
+		*error = 0;
+
+	return rv;
+}
+
+int
+rumpuser_net_setsockopt(int s, int level, int name,
+	const void *data, int dlen, int *error)
+{
+	socklen_t slen = dlen;
+	int rv;
+
+	rv = setsockopt(s, level, name, data, slen);
+	if (rv == -1)
+		*error = errno;
+	else
+		*error = 0;
+	return rv;
+}
Index: src/lib/librumpuser/rumpuser_pth.c
diff -u /dev/null src/lib/librumpuser/rumpuser_pth.c:1.1
--- /dev/null	Fri Feb 26 18:54:21 2010
+++ src/lib/librumpuser/rumpuser_pth.c	Fri Feb 26 18:54:20 2010
@@ -0,0 +1,526 @@
+/*	$NetBSD: rumpuser_pth.c,v 1.1 2010/02/26 18:54:20 pooka Exp $	*/
+
+/*
+ * Copyright (c) 2007-2010 Antti Kantee.  All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#if !defined(lint)
+__RCSID("$NetBSD: rumpuser_pth.c,v 1.1 2010/02/26 18:54:20 pooka Exp $");
+#endif /* !lint */
+
+#ifdef __linux__
+#define _XOPEN_SOURCE 500
+#define _BSD_SOURCE
+#define _FILE_OFFSET_BITS 64
+#endif
+
+#include <assert.h>
+#include <errno.h>
+#include <pthread.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include <unistd.h>
+
+#include <rump/rumpuser.h>
+
+#include "rumpuser_int.h"
+
+static pthread_key_t curlwpkey;
+
+#define NOFAIL(a) do {if (!(a)) abort();} while (/*CONSTCOND*/0)
+#define NOFAIL_ERRNO(a)							\
+do {									\
+	int fail_rv = (a);						\
+	if (fail_rv) {							\
+		printf("panic: rumpuser fatal failure %d (%s)\n",	\
+		    fail_rv, strerror(fail_rv));			\
+		    abort();						\
+	}								\
+} while (/*CONSTCOND*/0)
+
+#define RUMTX_INCRECURSION(mtx) ((mtx)->recursion++)
+#define RUMTX_DECRECURSION(mtx) ((mtx)->recursion--)
+struct rumpuser_mtx {
+	pthread_mutex_t pthmtx;
+	pthread_t owner;
+	unsigned recursion;
+};
+
+#define RURW_AMWRITER(rw) (pthread_equal(rw->writer, pthread_self())	\
+				&& rw->readers == -1)
+#define RURW_HASREAD(rw)  (rw->readers > 0)
+
+#define RURW_SETWRITE(rw)						\
+do {									\
+	assert(rw->readers == 0);					\
+	rw->writer = pthread_self();					\
+	rw->readers = -1;						\
+} while (/*CONSTCOND*/0)
+#define RURW_CLRWRITE(rw)						\
+do {									\
+	assert(rw->readers == -1 && RURW_AMWRITER(rw));			\
+	rw->readers = 0;						\
+} while (/*CONSTCOND*/0)
+#define RURW_INCREAD(rw)						\
+do {									\
+	pthread_spin_lock(&rw->spin);					\
+	assert(rw->readers >= 0);					\
+	++(rw)->readers;						\
+	pthread_spin_unlock(&rw->spin);					\
+} while (/*CONSTCOND*/0)
+#define RURW_DECREAD(rw)						\
+do {									\
+	pthread_spin_lock(&rw->spin);					\
+	assert(rw->readers > 0);					\
+	--(rw)->readers;						\
+	pthread_spin_unlock(&rw->spin);					\
+} while (/*CONSTCOND*/0)
+
+struct rumpuser_rw {
+	pthread_rwlock_t pthrw;
+	pthread_spinlock_t spin;
+	int readers;
+	pthread_t writer;
+};
+
+struct rumpuser_cv {
+	pthread_cond_t pthcv;
+	int nwaiters;
+};
+
+struct rumpuser_mtx rumpuser_aio_mtx;
+struct rumpuser_cv rumpuser_aio_cv;
+int rumpuser_aio_head, rumpuser_aio_tail;
+struct rumpuser_aio rumpuser_aios[N_AIOS];
+
+kernel_lockfn	rumpuser__klock;
+kernel_unlockfn	rumpuser__kunlock;
+int		rumpuser__wantthreads;
+
+void
+/*ARGSUSED*/
+rumpuser_biothread(void *arg)
+{
+	struct rumpuser_aio *rua;
+	rump_biodone_fn biodone = arg;
+	ssize_t rv;
+	int error, dummy;
+
+	/* unschedule from CPU.  we reschedule before running the interrupt */
+	rumpuser__kunlock(0, &dummy);
+	assert(dummy == 0);
+
+	NOFAIL_ERRNO(pthread_mutex_lock(&rumpuser_aio_mtx.pthmtx));
+	for (;;) {
+		while (rumpuser_aio_head == rumpuser_aio_tail) {
+			NOFAIL_ERRNO(pthread_cond_wait(&rumpuser_aio_cv.pthcv,
+			    &rumpuser_aio_mtx.pthmtx));
+		}
+
+		rua = &rumpuser_aios[rumpuser_aio_tail];
+		assert(rua->rua_bp != NULL);
+		pthread_mutex_unlock(&rumpuser_aio_mtx.pthmtx);
+
+		if (rua->rua_op & RUA_OP_READ) {
+			error = 0;
+			rv = pread(rua->rua_fd, rua->rua_data,
+			    rua->rua_dlen, rua->rua_off);
+			if (rv < 0) {
+				rv = 0;
+				error = errno;
+			}
+		} else {
+			error = 0;
+			rv = pwrite(rua->rua_fd, rua->rua_data,
+			    rua->rua_dlen, rua->rua_off);
+			if (rv < 0) {
+				rv = 0;
+				error = errno;
+			} else if (rua->rua_op & RUA_OP_SYNC) {
+#ifdef __NetBSD__
+				fsync_range(rua->rua_fd, FDATASYNC,
+				    rua->rua_off, rua->rua_dlen);
+#else
+				fsync(rua->rua_fd);
+#endif
+			}
+		}
+		rumpuser__klock(0);
+		biodone(rua->rua_bp, (size_t)rv, error);
+		rumpuser__kunlock(0, &dummy);
+
+		rua->rua_bp = NULL;
+
+		NOFAIL_ERRNO(pthread_mutex_lock(&rumpuser_aio_mtx.pthmtx));
+		rumpuser_aio_tail = (rumpuser_aio_tail+1) % N_AIOS;
+		pthread_cond_signal(&rumpuser_aio_cv.pthcv);
+	}
+
+	/*NOTREACHED*/
+	fprintf(stderr, "error: rumpuser_biothread reached unreachable\n");
+	abort();
+}
+
+void
+rumpuser_thrinit(kernel_lockfn lockfn, kernel_unlockfn unlockfn, int threads)
+{
+
+	pthread_mutex_init(&rumpuser_aio_mtx.pthmtx, NULL);
+	pthread_cond_init(&rumpuser_aio_cv.pthcv, NULL);
+
+	pthread_key_create(&curlwpkey, NULL);
+
+	rumpuser__klock = lockfn;
+	rumpuser__kunlock = unlockfn;
+	rumpuser__wantthreads = threads;
+}
+
+#if 0
+void
+rumpuser__thrdestroy(void)
+{
+
+	pthread_key_delete(curlwpkey);
+}
+#endif
+
+int
+rumpuser_thread_create(void *(*f)(void *), void *arg, const char *thrname)
+{
+	pthread_t ptid;
+	int rv;
+
+	rv = pthread_create(&ptid, NULL, f, arg);
+#ifdef __NetBSD__
+	if (rv == 0 && thrname)
+		pthread_setname_np(ptid, thrname, NULL);
+#endif
+
+	return rv;
+}
+
+__dead void
+rumpuser_thread_exit(void)
+{
+
+	pthread_exit(NULL);
+}
+
+void
+rumpuser_mutex_init(struct rumpuser_mtx **mtx)
+{
+	pthread_mutexattr_t att;
+
+	NOFAIL(*mtx = malloc(sizeof(struct rumpuser_mtx)));
+
+	pthread_mutexattr_init(&att);
+	pthread_mutexattr_settype(&att, PTHREAD_MUTEX_ERRORCHECK);
+	NOFAIL_ERRNO(pthread_mutex_init(&((*mtx)->pthmtx), &att));
+	pthread_mutexattr_destroy(&att);
+
+	(*mtx)->owner = NULL;
+	(*mtx)->recursion = 0;
+}
+
+void
+rumpuser_mutex_recursive_init(struct rumpuser_mtx **mtx)
+{
+	pthread_mutexattr_t mattr;
+
+	pthread_mutexattr_init(&mattr);
+	pthread_mutexattr_settype(&mattr, PTHREAD_MUTEX_RECURSIVE);
+
+	NOFAIL(*mtx = malloc(sizeof(struct rumpuser_mtx)));
+	NOFAIL_ERRNO(pthread_mutex_init(&((*mtx)->pthmtx), &mattr));
+	(*mtx)->owner = NULL;
+	(*mtx)->recursion = 0;
+
+	pthread_mutexattr_destroy(&mattr);
+}
+
+static void
+mtxenter(struct rumpuser_mtx *mtx)
+{
+
+	if (mtx->recursion++ == 0) {
+		assert(mtx->owner == NULL);
+		mtx->owner = pthread_self();
+	} else {
+		assert(pthread_equal(mtx->owner, pthread_self()));
+	}
+}
+
+static void
+mtxexit(struct rumpuser_mtx *mtx)
+{
+
+	assert(mtx->owner != NULL);
+	if (--mtx->recursion == 0)
+		mtx->owner = NULL;
+}
+
+void
+rumpuser_mutex_enter(struct rumpuser_mtx *mtx)
+{
+
+	if (pthread_mutex_trylock(&mtx->pthmtx) != 0)
+		KLOCK_WRAP(NOFAIL_ERRNO(pthread_mutex_lock(&mtx->pthmtx)));
+	mtxenter(mtx);
+}
+
+void
+rumpuser_mutex_enter_nowrap(struct rumpuser_mtx *mtx)
+{
+
+	NOFAIL_ERRNO(pthread_mutex_lock(&mtx->pthmtx));
+	mtxenter(mtx);
+}
+
+int
+rumpuser_mutex_tryenter(struct rumpuser_mtx *mtx)
+{
+	int rv;
+
+	rv = pthread_mutex_trylock(&mtx->pthmtx);
+	if (rv == 0) {
+		mtxenter(mtx);
+	}
+
+	return rv == 0;
+}
+
+void
+rumpuser_mutex_exit(struct rumpuser_mtx *mtx)
+{
+
+	mtxexit(mtx);
+	NOFAIL_ERRNO(pthread_mutex_unlock(&mtx->pthmtx));
+}
+
+void
+rumpuser_mutex_destroy(struct rumpuser_mtx *mtx)
+{
+
+	NOFAIL_ERRNO(pthread_mutex_destroy(&mtx->pthmtx));
+	free(mtx);
+}
+
+int
+rumpuser_mutex_held(struct rumpuser_mtx *mtx)
+{
+
+	return mtx->recursion && pthread_equal(mtx->owner, pthread_self());
+}
+
+void
+rumpuser_rw_init(struct rumpuser_rw **rw)
+{
+
+	NOFAIL(*rw = malloc(sizeof(struct rumpuser_rw)));
+	NOFAIL_ERRNO(pthread_rwlock_init(&((*rw)->pthrw), NULL));
+	NOFAIL_ERRNO(pthread_spin_init(&((*rw)->spin), PTHREAD_PROCESS_SHARED));
+	(*rw)->readers = 0;
+	(*rw)->writer = NULL;
+}
+
+void
+rumpuser_rw_enter(struct rumpuser_rw *rw, int iswrite)
+{
+
+	if (iswrite) {
+		if (pthread_rwlock_trywrlock(&rw->pthrw) != 0)
+			KLOCK_WRAP(NOFAIL_ERRNO(
+			    pthread_rwlock_wrlock(&rw->pthrw)));
+		RURW_SETWRITE(rw);
+	} else {
+		if (pthread_rwlock_tryrdlock(&rw->pthrw) != 0)
+			KLOCK_WRAP(NOFAIL_ERRNO(
+			    pthread_rwlock_rdlock(&rw->pthrw)));
+		RURW_INCREAD(rw);
+	}
+}
+
+int
+rumpuser_rw_tryenter(struct rumpuser_rw *rw, int iswrite)
+{
+	int rv;
+
+	if (iswrite) {
+		rv = pthread_rwlock_trywrlock(&rw->pthrw);
+		if (rv == 0)
+			RURW_SETWRITE(rw);
+	} else {
+		rv = pthread_rwlock_tryrdlock(&rw->pthrw);
+		if (rv == 0)
+			RURW_INCREAD(rw);
+	}
+
+	return rv == 0;
+}
+
+void
+rumpuser_rw_exit(struct rumpuser_rw *rw)
+{
+
+	if (RURW_HASREAD(rw))
+		RURW_DECREAD(rw);
+	else
+		RURW_CLRWRITE(rw);
+	NOFAIL_ERRNO(pthread_rwlock_unlock(&rw->pthrw));
+}
+
+void
+rumpuser_rw_destroy(struct rumpuser_rw *rw)
+{
+
+	NOFAIL_ERRNO(pthread_rwlock_destroy(&rw->pthrw));
+	NOFAIL_ERRNO(pthread_spin_destroy(&rw->spin));
+	free(rw);
+}
+
+int
+rumpuser_rw_held(struct rumpuser_rw *rw)
+{
+
+	return rw->readers != 0;
+}
+
+int
+rumpuser_rw_rdheld(struct rumpuser_rw *rw)
+{
+
+	return RURW_HASREAD(rw);
+}
+
+int
+rumpuser_rw_wrheld(struct rumpuser_rw *rw)
+{
+
+	return RURW_AMWRITER(rw);
+}
+
+void
+rumpuser_cv_init(struct rumpuser_cv **cv)
+{
+
+	NOFAIL(*cv = malloc(sizeof(struct rumpuser_cv)));
+	NOFAIL_ERRNO(pthread_cond_init(&((*cv)->pthcv), NULL));
+	(*cv)->nwaiters = 0;
+}
+
+void
+rumpuser_cv_destroy(struct rumpuser_cv *cv)
+{
+
+	NOFAIL_ERRNO(pthread_cond_destroy(&cv->pthcv));
+	free(cv);
+}
+
+void
+rumpuser_cv_wait(struct rumpuser_cv *cv, struct rumpuser_mtx *mtx)
+{
+
+	cv->nwaiters++;
+	assert(mtx->recursion == 1);
+	mtxexit(mtx);
+	KLOCK_WRAP(NOFAIL_ERRNO(pthread_cond_wait(&cv->pthcv, &mtx->pthmtx)));
+	mtxenter(mtx);
+	cv->nwaiters--;
+}
+
+void
+rumpuser_cv_wait_nowrap(struct rumpuser_cv *cv, struct rumpuser_mtx *mtx)
+{
+
+	cv->nwaiters++;
+	assert(mtx->recursion == 1);
+	mtxexit(mtx);
+	NOFAIL_ERRNO(pthread_cond_wait(&cv->pthcv, &mtx->pthmtx));
+	mtxenter(mtx);
+	cv->nwaiters--;
+}
+
+int
+rumpuser_cv_timedwait(struct rumpuser_cv *cv, struct rumpuser_mtx *mtx,
+	int64_t sec, int64_t nsec)
+{
+	struct timespec ts;
+	int rv;
+
+	/* LINTED */
+	ts.tv_sec = sec; ts.tv_nsec = nsec;
+
+	cv->nwaiters++;
+	mtxexit(mtx);
+	KLOCK_WRAP(rv = pthread_cond_timedwait(&cv->pthcv, &mtx->pthmtx, &ts));
+	mtxenter(mtx);
+	cv->nwaiters--;
+	if (rv != 0 && rv != ETIMEDOUT)
+		abort();
+
+	return rv == ETIMEDOUT;
+}
+
+void
+rumpuser_cv_signal(struct rumpuser_cv *cv)
+{
+
+	NOFAIL_ERRNO(pthread_cond_signal(&cv->pthcv));
+}
+
+void
+rumpuser_cv_broadcast(struct rumpuser_cv *cv)
+{
+
+	NOFAIL_ERRNO(pthread_cond_broadcast(&cv->pthcv));
+}
+
+int
+rumpuser_cv_has_waiters(struct rumpuser_cv *cv)
+{
+
+	return cv->nwaiters;
+}
+
+/*
+ * curlwp
+ */
+
+void
+rumpuser_set_curlwp(struct lwp *l)
+{
+
+	assert(pthread_getspecific(curlwpkey) == NULL || l == NULL);
+	pthread_setspecific(curlwpkey, l);
+}
+
+struct lwp *
+rumpuser_get_curlwp(void)
+{
+
+	return pthread_getspecific(curlwpkey);
+}
Index: src/lib/librumpuser/rumpuser_pth_dummy.c
diff -u /dev/null src/lib/librumpuser/rumpuser_pth_dummy.c:1.1
--- /dev/null	Fri Feb 26 18:54:21 2010
+++ src/lib/librumpuser/rumpuser_pth_dummy.c	Fri Feb 26 18:54:20 2010
@@ -0,0 +1,296 @@
+/*	$NetBSD: rumpuser_pth_dummy.c,v 1.1 2010/02/26 18:54:20 pooka Exp $	*/
+
+/*
+ * Copyright (c) 2009 Antti Kantee.  All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#if !defined(lint)
+__RCSID("$NetBSD: rumpuser_pth_dummy.c,v 1.1 2010/02/26 18:54:20 pooka Exp $");
+#endif /* !lint */
+
+#include <sys/time.h>
+
+#include <assert.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+
+#include <rump/rumpuser.h>
+
+#include "rumpuser_int.h"
+
+struct rumpuser_cv {};
+
+struct rumpuser_mtx {
+	int v;
+};
+
+struct rumpuser_rw {
+	int v;
+};
+
+struct rumpuser_mtx rumpuser_aio_mtx;
+struct rumpuser_cv rumpuser_aio_cv;
+int rumpuser_aio_head, rumpuser_aio_tail;
+struct rumpuser_aio rumpuser_aios[N_AIOS];
+
+void donada(int);
+/*ARGSUSED*/
+void donada(int arg) {}
+void dounnada(int, int *);
+/*ARGSUSED*/
+void dounnada(int arg, int *ap) {}
+kernel_lockfn   rumpuser__klock = donada;
+kernel_unlockfn rumpuser__kunlock = dounnada;
+
+/*ARGSUSED*/
+void
+rumpuser_thrinit(kernel_lockfn lockfn, kernel_unlockfn unlockfn, int threads)
+{
+
+}
+
+/*ARGSUSED*/
+void
+rumpuser_biothread(void *arg)
+{
+
+	fprintf(stderr, "rumpuser: threads not available\n");
+	abort();
+}
+
+/*ARGSUSED*/
+int
+rumpuser_thread_create(void *(*f)(void *), void *arg, const char *thrname)
+{
+
+	fprintf(stderr, "rumpuser: threads not available\n");
+	abort();
+	return 0;
+}
+
+void
+rumpuser_thread_exit(void)
+{
+
+}
+
+void
+rumpuser_mutex_init(struct rumpuser_mtx **mtx)
+{
+
+	*mtx = calloc(1, sizeof(struct rumpuser_mtx));
+}
+
+void
+rumpuser_mutex_recursive_init(struct rumpuser_mtx **mtx)
+{
+
+	rumpuser_mutex_init(mtx);
+}
+
+void
+rumpuser_mutex_enter(struct rumpuser_mtx *mtx)
+{
+
+	mtx->v++;
+}
+
+int
+rumpuser_mutex_tryenter(struct rumpuser_mtx *mtx)
+{
+
+	mtx->v++;
+	return 1;
+}
+
+void
+rumpuser_mutex_exit(struct rumpuser_mtx *mtx)
+{
+
+	mtx->v--;
+}
+
+void
+rumpuser_mutex_destroy(struct rumpuser_mtx *mtx)
+{
+
+	free(mtx);
+}
+
+int
+rumpuser_mutex_held(struct rumpuser_mtx *mtx)
+{
+
+	return mtx->v;
+}
+
+void
+rumpuser_rw_init(struct rumpuser_rw **rw)
+{
+
+	*rw = calloc(1, sizeof(struct rumpuser_rw));
+}
+
+void
+rumpuser_rw_enter(struct rumpuser_rw *rw, int write)
+{
+
+	if (write) {
+		rw->v++;
+		assert(rw->v == 1);
+	} else {
+		assert(rw->v <= 0);
+		rw->v--;
+	}
+}
+
+int
+rumpuser_rw_tryenter(struct rumpuser_rw *rw, int write)
+{
+
+	rumpuser_rw_enter(rw, write);
+	return 1;
+}
+
+void
+rumpuser_rw_exit(struct rumpuser_rw *rw)
+{
+
+	if (rw->v > 0) {
+		assert(rw->v == 1);
+		rw->v--;
+	} else {
+		rw->v++;
+	}
+}
+
+void
+rumpuser_rw_destroy(struct rumpuser_rw *rw)
+{
+
+	free(rw);
+}
+
+int
+rumpuser_rw_held(struct rumpuser_rw *rw)
+{
+
+	return rw->v != 0;
+}
+
+int
+rumpuser_rw_rdheld(struct rumpuser_rw *rw)
+{
+
+	return rw->v < 0;
+}
+
+int
+rumpuser_rw_wrheld(struct rumpuser_rw *rw)
+{
+
+	return rw->v > 0;
+}
+
+/*ARGSUSED*/
+void
+rumpuser_cv_init(struct rumpuser_cv **cv)
+{
+
+}
+
+/*ARGSUSED*/
+void
+rumpuser_cv_destroy(struct rumpuser_cv *cv)
+{
+
+}
+
+/*ARGSUSED*/
+void
+rumpuser_cv_wait(struct rumpuser_cv *cv, struct rumpuser_mtx *mtx)
+{
+
+}
+
+/*ARGSUSED*/
+int
+rumpuser_cv_timedwait(struct rumpuser_cv *cv, struct rumpuser_mtx *mtx,
+	int64_t sec, int64_t nsec)
+{
+	struct timespec ts;
+
+	/*LINTED*/
+	ts.tv_sec = sec;
+	/*LINTED*/
+	ts.tv_nsec = nsec;
+
+	nanosleep(&ts, NULL);
+	return 0;
+}
+
+/*ARGSUSED*/
+void
+rumpuser_cv_signal(struct rumpuser_cv *cv)
+{
+
+}
+
+/*ARGSUSED*/
+void
+rumpuser_cv_broadcast(struct rumpuser_cv *cv)
+{
+
+}
+
+/*ARGSUSED*/
+int
+rumpuser_cv_has_waiters(struct rumpuser_cv *cv)
+{
+
+	return 0;
+}
+
+/*
+ * curlwp
+ */
+
+static struct lwp *curlwp;
+void
+rumpuser_set_curlwp(struct lwp *l)
+{
+
+	curlwp = l;
+}
+
+struct lwp *
+rumpuser_get_curlwp(void)
+{
+
+	return curlwp;
+}
Index: src/lib/librumpuser/shlib_version
diff -u /dev/null src/lib/librumpuser/shlib_version:1.1
--- /dev/null	Fri Feb 26 18:54:21 2010
+++ src/lib/librumpuser/shlib_version	Fri Feb 26 18:54:20 2010
@@ -0,0 +1,4 @@
+#	$NetBSD: shlib_version,v 1.1 2010/02/26 18:54:20 pooka Exp $
+#
+major=0
+minor=0

Reply via email to