xref: /netbsd-src/lib/librumpuser/rumpuser.c (revision bbde328be4e75ea9ad02e9715ea13ca54b797ada)
1 /*	$NetBSD: rumpuser.c,v 1.4 2010/04/28 00:33:45 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.4 2010/04/28 00:33:45 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 canfail, const char *func, int line)
197 {
198 	void *rv;
199 
200 	rv = malloc(howmuch);
201 	if (rv == NULL && canfail == 0) {
202 		warn("malloc failed %s (%d)", func, line);
203 		abort();
204 	}
205 
206 	return rv;
207 }
208 
209 void *
210 rumpuser__realloc(void *ptr, size_t howmuch, int canfail,
211 	const char *func, int line)
212 {
213 	void *rv;
214 
215 	rv = realloc(ptr, howmuch);
216 	if (rv == NULL && canfail == 0) {
217 		warn("realloc failed %s (%d)", func, line);
218 		abort();
219 	}
220 
221 	return rv;
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