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