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