1 /*- 2 * SPDX-License-Identifier: BSD-3-Clause 3 * 4 * Copyright (c) 1980, 1993 5 * The Regents of the University of California. All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. Neither the name of the University nor the names of its contributors 16 * may be used to endorse or promote products derived from this software 17 * without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 * SUCH DAMAGE. 30 */ 31 32 #include <sys/param.h> 33 #include <sys/disk.h> 34 #include <sys/disklabel.h> 35 #include <sys/mdioctl.h> 36 #include <sys/stat.h> 37 #include <sys/sysctl.h> 38 #include <sys/wait.h> 39 #include <vm/vm_param.h> 40 #include <vm/swap_pager.h> 41 42 #include <err.h> 43 #include <errno.h> 44 #include <fcntl.h> 45 #include <fnmatch.h> 46 #include <fstab.h> 47 #include <libgen.h> 48 #include <libutil.h> 49 #include <limits.h> 50 #include <paths.h> 51 #include <stdarg.h> 52 #include <stdio.h> 53 #include <stdlib.h> 54 #include <string.h> 55 #include <unistd.h> 56 57 #include "extern.h" 58 59 #define _PATH_GNOP "/sbin/gnop" 60 61 static void usage(void) __dead2; 62 static const char *swap_on_off(const char *, int, char *); 63 static const char *swap_on_off_gbde(const char *, int); 64 static const char *swap_on_off_geli(const char *, char *, int); 65 static const char *swap_on_off_linux(const char *, int); 66 static const char *swap_on_off_md(const char *, char *, int); 67 static const char *swap_on_off_sfile(const char *, int); 68 static void swaplist(int, int, int); 69 static int run_cmd(int *, const char *, ...) __printflike(2, 3); 70 71 static enum { SWAPON, SWAPOFF, SWAPCTL } orig_prog, which_prog = SWAPCTL; 72 73 static int Eflag, fflag, qflag; 74 75 int 76 main(int argc, char **argv) 77 { 78 struct fstab *fsp; 79 const char *swfile; 80 char *ptr; 81 int ret, ch, doall; 82 int sflag, lflag, late, hflag; 83 const char *etc_fstab; 84 85 sflag = lflag = late = hflag = 0; 86 if ((ptr = strrchr(argv[0], '/')) == NULL) 87 ptr = argv[0]; 88 if (strstr(ptr, "swapon") != NULL) 89 which_prog = SWAPON; 90 else if (strstr(ptr, "swapoff") != NULL) 91 which_prog = SWAPOFF; 92 orig_prog = which_prog; 93 94 doall = 0; 95 etc_fstab = NULL; 96 while ((ch = getopt(argc, argv, "AadEfghklLmqsUF:")) != -1) { 97 switch(ch) { 98 case 'A': 99 if (which_prog == SWAPCTL) { 100 doall = 1; 101 which_prog = SWAPON; 102 } else 103 usage(); 104 break; 105 case 'a': 106 if (which_prog == SWAPON || which_prog == SWAPOFF) 107 doall = 1; 108 else 109 which_prog = SWAPON; 110 break; 111 case 'd': 112 if (which_prog == SWAPCTL) 113 which_prog = SWAPOFF; 114 else 115 usage(); 116 break; 117 case 'E': 118 if (which_prog == SWAPON) 119 Eflag = 2; 120 else 121 usage(); 122 break; 123 case 'f': 124 if (which_prog == SWAPOFF) 125 fflag = 1; 126 else 127 usage(); 128 break; 129 case 'g': 130 hflag = 'G'; 131 break; 132 case 'h': 133 hflag = 'H'; 134 break; 135 case 'k': 136 hflag = 'K'; 137 break; 138 case 'l': 139 lflag = 1; 140 break; 141 case 'L': 142 late = 1; 143 break; 144 case 'm': 145 hflag = 'M'; 146 break; 147 case 'q': 148 if (which_prog == SWAPON || which_prog == SWAPOFF) 149 qflag = 1; 150 break; 151 case 's': 152 sflag = 1; 153 break; 154 case 'U': 155 if (which_prog == SWAPCTL) { 156 doall = 1; 157 which_prog = SWAPOFF; 158 } else 159 usage(); 160 break; 161 case 'F': 162 etc_fstab = optarg; 163 break; 164 case '?': 165 default: 166 usage(); 167 } 168 } 169 argv += optind; 170 171 ret = 0; 172 swfile = NULL; 173 if (etc_fstab != NULL) 174 setfstab(etc_fstab); 175 if (which_prog == SWAPON || which_prog == SWAPOFF) { 176 if (doall) { 177 while ((fsp = getfsent()) != NULL) { 178 if (strcmp(fsp->fs_type, FSTAB_SW) != 0) 179 continue; 180 if (strstr(fsp->fs_mntops, "noauto") != NULL) 181 continue; 182 if (which_prog != SWAPOFF && 183 strstr(fsp->fs_mntops, "late") && 184 late == 0) 185 continue; 186 if (which_prog == SWAPOFF && 187 strstr(fsp->fs_mntops, "late") == NULL && 188 late != 0) 189 continue; 190 Eflag |= (strstr(fsp->fs_mntops, "trimonce") != NULL); 191 swfile = swap_on_off(fsp->fs_spec, 1, 192 fsp->fs_mntops); 193 Eflag &= ~1; 194 if (swfile == NULL) { 195 ret = 1; 196 continue; 197 } 198 if (qflag == 0) { 199 printf("%s: %sing %s as swap device\n", 200 getprogname(), 201 (which_prog == SWAPOFF) ? 202 "remov" : "add", swfile); 203 } 204 } 205 } else if (*argv == NULL) 206 usage(); 207 for (; *argv; ++argv) { 208 swfile = swap_on_off(*argv, 0, NULL); 209 if (swfile == NULL) { 210 ret = 1; 211 continue; 212 } 213 if (orig_prog == SWAPCTL) { 214 printf("%s: %sing %s as swap device\n", 215 getprogname(), 216 (which_prog == SWAPOFF) ? "remov" : "add", 217 swfile); 218 } 219 } 220 } else { 221 if (lflag || sflag) 222 swaplist(lflag, sflag, hflag); 223 else 224 usage(); 225 } 226 exit(ret); 227 } 228 229 static const char * 230 swap_on_off(const char *name, int doingall, char *mntops) 231 { 232 char *base, *basebuf; 233 234 /* Swap on vnode-backed md(4) device. */ 235 if (mntops != NULL && 236 (fnmatch(_PATH_DEV MD_NAME "[0-9]*", name, 0) == 0 || 237 fnmatch(MD_NAME "[0-9]*", name, 0) == 0 || 238 strncmp(_PATH_DEV MD_NAME, name, 239 sizeof(_PATH_DEV) + sizeof(MD_NAME)) == 0 || 240 strncmp(MD_NAME, name, sizeof(MD_NAME)) == 0)) 241 return (swap_on_off_md(name, mntops, doingall)); 242 243 basebuf = strdup(name); 244 base = basename(basebuf); 245 246 /* Swap on encrypted device by GEOM_BDE. */ 247 if (fnmatch("*.bde", base, 0) == 0) { 248 free(basebuf); 249 return (swap_on_off_gbde(name, doingall)); 250 } 251 252 /* Swap on encrypted device by GEOM_ELI. */ 253 if (fnmatch("*.eli", base, 0) == 0) { 254 free(basebuf); 255 return (swap_on_off_geli(name, mntops, doingall)); 256 } 257 258 free(basebuf); 259 260 /* Linux swap */ 261 if (is_linux_swap(name)) 262 return (swap_on_off_linux(name, doingall)); 263 264 /* Swap on special file. */ 265 return (swap_on_off_sfile(name, doingall)); 266 } 267 268 /* Strip off .bde or .eli suffix from swap device name */ 269 static char * 270 swap_basename(const char *name) 271 { 272 char *dname, *p; 273 274 dname = strdup(name); 275 p = strrchr(dname, '.'); 276 /* assert(p != NULL); */ 277 *p = '\0'; 278 279 return (dname); 280 } 281 282 static const char * 283 swap_on_off_gbde(const char *name, int doingall) 284 { 285 const char *ret; 286 char pass[64 * 2 + 1]; 287 unsigned char bpass[64]; 288 char *dname; 289 int i, error; 290 291 dname = swap_basename(name); 292 if (dname == NULL) 293 return (NULL); 294 295 if (which_prog == SWAPON) { 296 arc4random_buf(bpass, sizeof(bpass)); 297 for (i = 0; i < (int)sizeof(bpass); i++) 298 sprintf(&pass[2 * i], "%02x", bpass[i]); 299 pass[sizeof(pass) - 1] = '\0'; 300 301 error = run_cmd(NULL, "%s init %s -P %s", _PATH_GBDE, 302 dname, pass); 303 if (error) { 304 /* bde device found. Ignore it. */ 305 free(dname); 306 if (qflag == 0) 307 warnx("%s: Device already in use", name); 308 return (NULL); 309 } 310 error = run_cmd(NULL, "%s attach %s -p %s", _PATH_GBDE, 311 dname, pass); 312 free(dname); 313 if (error) { 314 warnx("gbde (attach) error: %s", name); 315 return (NULL); 316 } 317 } 318 319 ret = swap_on_off_sfile(name, doingall); 320 321 if (which_prog == SWAPOFF) { 322 error = run_cmd(NULL, "%s detach %s", _PATH_GBDE, dname); 323 free(dname); 324 if (error) { 325 /* bde device not found. Ignore it. */ 326 if (qflag == 0) 327 warnx("%s: Device not found", name); 328 return (NULL); 329 } 330 } 331 332 return (ret); 333 } 334 335 /* Build geli(8) arguments from mntops */ 336 static char * 337 swap_on_geli_args(const char *mntops) 338 { 339 const char *aalgo, *ealgo, *keylen_str, *sectorsize_str; 340 const char *aflag, *eflag, *lflag, *Tflag, *sflag; 341 char *p, *args, *token, *string, *ops; 342 int pagesize; 343 size_t pagesize_len; 344 u_long ul; 345 346 /* Use built-in defaults for geli(8). */ 347 aalgo = ealgo = keylen_str = ""; 348 aflag = eflag = lflag = Tflag = ""; 349 350 /* We will always specify sectorsize. */ 351 sflag = " -s "; 352 sectorsize_str = NULL; 353 354 if (mntops != NULL) { 355 string = ops = strdup(mntops); 356 357 while ((token = strsep(&string, ",")) != NULL) { 358 if ((p = strstr(token, "aalgo=")) == token) { 359 aalgo = p + sizeof("aalgo=") - 1; 360 aflag = " -a "; 361 } else if ((p = strstr(token, "ealgo=")) == token) { 362 ealgo = p + sizeof("ealgo=") - 1; 363 eflag = " -e "; 364 } else if ((p = strstr(token, "keylen=")) == token) { 365 keylen_str = p + sizeof("keylen=") - 1; 366 errno = 0; 367 ul = strtoul(keylen_str, &p, 10); 368 if (errno == 0) { 369 if (*p != '\0' || ul > INT_MAX) 370 errno = EINVAL; 371 } 372 if (errno) { 373 warn("Invalid keylen: %s", keylen_str); 374 free(ops); 375 return (NULL); 376 } 377 lflag = " -l "; 378 } else if ((p = strstr(token, "sectorsize=")) == token) { 379 sectorsize_str = p + sizeof("sectorsize=") - 1; 380 errno = 0; 381 ul = strtoul(sectorsize_str, &p, 10); 382 if (errno == 0) { 383 if (*p != '\0' || ul > INT_MAX) 384 errno = EINVAL; 385 } 386 if (errno) { 387 warn("Invalid sectorsize: %s", 388 sectorsize_str); 389 free(ops); 390 return (NULL); 391 } 392 } else if (strcmp(token, "notrim") == 0) { 393 if (Eflag) { 394 warn("Options \"notrim\" and " 395 "\"trimonce\" conflict"); 396 free(ops); 397 return (NULL); 398 } 399 Tflag = " -T "; 400 } else if (strcmp(token, "late") == 0) { 401 /* ignore known option */ 402 } else if (strcmp(token, "noauto") == 0) { 403 /* ignore known option */ 404 } else if (strcmp(token, "sw") == 0) { 405 /* ignore known option */ 406 } else if (strcmp(token, "trimonce") == 0) { 407 /* ignore known option */ 408 } else { 409 warnx("Invalid option: %s", token); 410 free(ops); 411 return (NULL); 412 } 413 } 414 } else 415 ops = NULL; 416 417 /* 418 * If we do not have a sector size at this point, fill in 419 * pagesize as sector size. 420 */ 421 if (sectorsize_str == NULL) { 422 /* Use pagesize as default sectorsize. */ 423 pagesize = getpagesize(); 424 pagesize_len = snprintf(NULL, 0, "%d", pagesize) + 1; 425 p = alloca(pagesize_len); 426 snprintf(p, pagesize_len, "%d", pagesize); 427 sectorsize_str = p; 428 } 429 430 (void)asprintf(&args, "%s%s%s%s%s%s%s%s%s -d", 431 aflag, aalgo, eflag, ealgo, lflag, keylen_str, Tflag, 432 sflag, sectorsize_str); 433 434 free(ops); 435 return (args); 436 } 437 438 static const char * 439 swap_on_off_geli(const char *name, char *mntops, int doingall) 440 { 441 struct stat sb; 442 char *dname, *args; 443 int error; 444 445 error = stat(name, &sb); 446 447 if (which_prog == SWAPON) do { 448 /* Skip if the .eli device already exists. */ 449 if (error == 0) 450 break; 451 452 args = swap_on_geli_args(mntops); 453 if (args == NULL) 454 return (NULL); 455 456 dname = swap_basename(name); 457 if (dname == NULL) { 458 free(args); 459 return (NULL); 460 } 461 462 error = run_cmd(NULL, "%s onetime%s %s", _PATH_GELI, args, 463 dname); 464 465 free(dname); 466 free(args); 467 468 if (error) { 469 /* error occurred during creation. */ 470 if (qflag == 0) 471 warnx("%s: Invalid parameters", name); 472 return (NULL); 473 } 474 } while (0); 475 476 return (swap_on_off_sfile(name, doingall)); 477 } 478 479 static const char * 480 swap_on_off_linux(const char *name, int doingall) 481 { 482 const char *ret; 483 char *nopname; 484 size_t nopnamelen; 485 int error; 486 487 if (which_prog == SWAPON) { 488 /* Skip the header for Linux swap partitions */ 489 error = run_cmd(NULL, "%s create -o 4096 %s", _PATH_GNOP, 490 name); 491 if (error) { 492 warnx("gnop (create) error: %s", name); 493 return (NULL); 494 } 495 } 496 497 /* Append ".nop" to name */ 498 nopnamelen = strlen(name) + sizeof(".nop"); 499 nopname = (char *) malloc(nopnamelen); 500 if (nopname == NULL) 501 err(1, "malloc()"); 502 (void)strlcpy(nopname, name, nopnamelen); 503 (void)strlcat(nopname, ".nop", nopnamelen); 504 505 ret = swap_on_off_sfile(nopname, doingall); 506 507 if (which_prog == SWAPOFF) { 508 error = run_cmd(NULL, "%s destroy %s", _PATH_GNOP, nopname); 509 if (error) { 510 warnx("gnop (destroy) error: %s", name); 511 free(nopname); 512 return (NULL); 513 } 514 } 515 516 free(nopname); 517 518 return (ret); 519 } 520 521 static const char * 522 swap_on_off_md(const char *name, char *mntops, int doingall) 523 { 524 FILE *sfd; 525 int fd, mdunit, error; 526 const char *ret; 527 static char mdpath[PATH_MAX], linebuf[PATH_MAX]; 528 char *p, *vnodefile; 529 size_t linelen; 530 u_long ul; 531 532 fd = -1; 533 sfd = NULL; 534 if (strlen(name) == (sizeof(MD_NAME) - 1)) 535 mdunit = -1; 536 else { 537 errno = 0; 538 ul = strtoul(name + 2, &p, 10); 539 if (errno == 0) { 540 if (*p != '\0' || ul > INT_MAX) 541 errno = EINVAL; 542 } 543 if (errno) { 544 warn("Bad device unit: %s", name); 545 return (NULL); 546 } 547 mdunit = (int)ul; 548 } 549 550 vnodefile = NULL; 551 if ((p = strstr(mntops, "file=")) != NULL) { 552 vnodefile = strdup(p + sizeof("file=") - 1); 553 p = strchr(vnodefile, ','); 554 if (p != NULL) 555 *p = '\0'; 556 } 557 if (vnodefile == NULL) { 558 warnx("file option not found for %s", name); 559 return (NULL); 560 } 561 562 if (which_prog == SWAPON) { 563 if (mdunit == -1) { 564 error = run_cmd(&fd, "%s -l -n -f %s", 565 _PATH_MDCONFIG, vnodefile); 566 if (error == 0) { 567 /* md device found. Ignore it. */ 568 close(fd); 569 if (!qflag) 570 warnx("%s: Device already in use", 571 vnodefile); 572 free(vnodefile); 573 return (NULL); 574 } 575 error = run_cmd(&fd, "%s -a -t vnode -n -f %s", 576 _PATH_MDCONFIG, vnodefile); 577 if (error) { 578 warnx("mdconfig (attach) error: file=%s", 579 vnodefile); 580 free(vnodefile); 581 return (NULL); 582 } 583 sfd = fdopen(fd, "r"); 584 if (sfd == NULL) { 585 warn("mdconfig (attach) fdopen error"); 586 ret = NULL; 587 goto err; 588 } 589 p = fgetln(sfd, &linelen); 590 if (p == NULL || 591 (linelen < 2 || linelen > sizeof(linebuf))) { 592 warn("mdconfig (attach) unexpected output"); 593 ret = NULL; 594 goto err; 595 } 596 strlcpy(linebuf, p, linelen); 597 errno = 0; 598 ul = strtoul(linebuf, &p, 10); 599 if (errno == 0) { 600 if (*p != '\0' || ul > INT_MAX) 601 errno = EINVAL; 602 } 603 if (errno) { 604 warn("mdconfig (attach) unexpected output: %s", 605 linebuf); 606 ret = NULL; 607 goto err; 608 } 609 mdunit = (int)ul; 610 } else { 611 error = run_cmd(&fd, "%s -l -n -f %s -u %d", 612 _PATH_MDCONFIG, vnodefile, mdunit); 613 if (error == 0) { 614 /* md device found. Ignore it. */ 615 close(fd); 616 if (qflag == 0) 617 warnx("md%d on %s: Device already " 618 "in use", mdunit, vnodefile); 619 free(vnodefile); 620 return (NULL); 621 } 622 error = run_cmd(NULL, "%s -a -t vnode -u %d -f %s", 623 _PATH_MDCONFIG, mdunit, vnodefile); 624 if (error) { 625 warnx("mdconfig (attach) error: " 626 "md%d on file=%s", mdunit, vnodefile); 627 free(vnodefile); 628 return (NULL); 629 } 630 } 631 } else /* SWAPOFF */ { 632 if (mdunit == -1) { 633 error = run_cmd(&fd, "%s -l -n -f %s", 634 _PATH_MDCONFIG, vnodefile); 635 if (error) { 636 /* md device not found. Ignore it. */ 637 close(fd); 638 if (!qflag) 639 warnx("md on %s: Device not found", 640 vnodefile); 641 free(vnodefile); 642 return (NULL); 643 } 644 sfd = fdopen(fd, "r"); 645 if (sfd == NULL) { 646 warn("mdconfig (list) fdopen error"); 647 ret = NULL; 648 goto err; 649 } 650 p = fgetln(sfd, &linelen); 651 if (p == NULL || 652 (linelen < 2 || linelen > sizeof(linebuf))) { 653 warn("mdconfig (list) unexpected output"); 654 ret = NULL; 655 goto err; 656 } 657 strlcpy(linebuf, p, linelen); 658 p = strchr(linebuf, ' '); 659 if (p != NULL) 660 *p = '\0'; 661 errno = 0; 662 ul = strtoul(linebuf, &p, 10); 663 if (errno == 0) { 664 if (*p != '\0' || ul > INT_MAX) 665 errno = EINVAL; 666 } 667 if (errno) { 668 warn("mdconfig (list) unexpected output: %s", 669 linebuf); 670 ret = NULL; 671 goto err; 672 } 673 mdunit = (int)ul; 674 } else { 675 error = run_cmd(&fd, "%s -l -n -f %s -u %d", 676 _PATH_MDCONFIG, vnodefile, mdunit); 677 if (error) { 678 /* md device not found. Ignore it. */ 679 close(fd); 680 if (!qflag) 681 warnx("md%d on %s: Device not found", 682 mdunit, vnodefile); 683 free(vnodefile); 684 return (NULL); 685 } 686 } 687 } 688 snprintf(mdpath, sizeof(mdpath), "%s%s%d", _PATH_DEV, 689 MD_NAME, mdunit); 690 mdpath[sizeof(mdpath) - 1] = '\0'; 691 ret = swap_on_off_sfile(mdpath, doingall); 692 693 if (which_prog == SWAPOFF) { 694 if (ret != NULL) { 695 error = run_cmd(NULL, "%s -d -u %d", 696 _PATH_MDCONFIG, mdunit); 697 if (error) 698 warn("mdconfig (detach) detach failed: %s%s%d", 699 _PATH_DEV, MD_NAME, mdunit); 700 } 701 } 702 err: 703 if (sfd != NULL) 704 fclose(sfd); 705 if (fd != -1) 706 close(fd); 707 free(vnodefile); 708 return (ret); 709 } 710 711 static int 712 run_cmd(int *ofd, const char *cmdline, ...) 713 { 714 va_list ap; 715 char **argv, **argvp, *cmd, *p; 716 int argc, pid, status, rv; 717 int pfd[2], nfd, dup2dn; 718 719 va_start(ap, cmdline); 720 rv = vasprintf(&cmd, cmdline, ap); 721 if (rv == -1) { 722 warn("%s", __func__); 723 va_end(ap); 724 return (rv); 725 } 726 va_end(ap); 727 728 for (argc = 1, p = cmd; (p = strchr(p, ' ')) != NULL; p++) 729 argc++; 730 argv = (char **)malloc(sizeof(*argv) * (argc + 1)); 731 for (p = cmd, argvp = argv; (*argvp = strsep(&p, " ")) != NULL;) 732 if (**argvp != '\0' && (++argvp > &argv[argc])) { 733 *argvp = NULL; 734 break; 735 } 736 /* The argv array ends up NULL-terminated here. */ 737 #if 0 738 { 739 int i; 740 741 fprintf(stderr, "DEBUG: running:"); 742 /* Should be equivalent to 'cmd' (before strsep, of course). */ 743 for (i = 0; argv[i] != NULL; i++) 744 fprintf(stderr, " %s", argv[i]); 745 fprintf(stderr, "\n"); 746 } 747 #endif 748 dup2dn = 1; 749 if (ofd != NULL) { 750 if (pipe(&pfd[0]) == -1) { 751 warn("%s: pipe", __func__); 752 return (-1); 753 } 754 *ofd = pfd[0]; 755 dup2dn = 0; 756 } 757 pid = fork(); 758 switch (pid) { 759 case 0: 760 /* Child process. */ 761 if (ofd != NULL) 762 if (dup2(pfd[1], STDOUT_FILENO) < 0) 763 err(1, "dup2 in %s", __func__); 764 nfd = open(_PATH_DEVNULL, O_RDWR); 765 if (nfd == -1) 766 err(1, "%s: open %s", __func__, _PATH_DEVNULL); 767 if (dup2(nfd, STDIN_FILENO) < 0) 768 err(1, "%s: dup2", __func__); 769 if (dup2dn && dup2(nfd, STDOUT_FILENO) < 0) 770 err(1, "%s: dup2", __func__); 771 if (dup2(nfd, STDERR_FILENO) < 0) 772 err(1, "%s: dup2", __func__); 773 execv(argv[0], argv); 774 warn("exec: %s", argv[0]); 775 _exit(-1); 776 case -1: 777 err(1, "%s: fork", __func__); 778 } 779 free(cmd); 780 free(argv); 781 while (waitpid(pid, &status, 0) != pid) 782 ; 783 return (WEXITSTATUS(status)); 784 } 785 786 static int 787 swapon_trim(const char *name) 788 { 789 struct stat sb; 790 off_t ioarg[2], sz; 791 int error, fd; 792 793 /* Open a descriptor to create a consumer of the device. */ 794 fd = open(name, O_WRONLY); 795 if (fd < 0) 796 errx(1, "Cannot open %s", name); 797 /* Find the device size. */ 798 if (fstat(fd, &sb) < 0) 799 errx(1, "Cannot stat %s", name); 800 if (S_ISREG(sb.st_mode)) 801 sz = sb.st_size; 802 else if (S_ISCHR(sb.st_mode)) { 803 if (ioctl(fd, DIOCGMEDIASIZE, &sz) != 0) 804 err(1, "ioctl(DIOCGMEDIASIZE)"); 805 } else 806 errx(1, "%s has an invalid file type", name); 807 /* Trim the device. */ 808 ioarg[0] = BBSIZE; 809 ioarg[1] = sz - BBSIZE; 810 if (ioctl(fd, DIOCGDELETE, ioarg) != 0) 811 warn("ioctl(DIOCGDELETE)"); 812 813 /* Start using the device for swapping, creating a second consumer. */ 814 error = swapon(name); 815 816 /* 817 * Do not close the device until the swap pager has attempted to create 818 * another consumer. For GELI devices created with the 'detach -l' 819 * option, removing the last consumer causes the device to be detached 820 * - that is, to disappear. This ordering ensures that the device will 821 * not be detached until swapoff is called. 822 */ 823 close(fd); 824 return (error); 825 } 826 827 static const char * 828 swap_on_off_sfile(const char *name, int doingall) 829 { 830 int error; 831 832 if (which_prog == SWAPON) 833 error = Eflag ? swapon_trim(name) : swapon(name); 834 else /* SWAPOFF */ 835 error = swapoff(name, fflag ? SWAPOFF_FORCE : 0); 836 837 if (error == -1) { 838 switch (errno) { 839 case EBUSY: 840 if (doingall == 0) 841 warnx("%s: Device already in use", name); 842 break; 843 case EINVAL: 844 if (which_prog == SWAPON) 845 warnx("%s: NSWAPDEV limit reached", name); 846 else if (doingall == 0) 847 warn("%s", name); 848 break; 849 default: 850 warn("%s", name); 851 break; 852 } 853 return (NULL); 854 } 855 return (name); 856 } 857 858 static void 859 usage(void) 860 { 861 862 fprintf(stderr, "usage: %s ", getprogname()); 863 switch(orig_prog) { 864 case SWAPON: 865 fprintf(stderr, "[-F fstab] -aLq | [-E] file ...\n"); 866 break; 867 case SWAPOFF: 868 fprintf(stderr, "[-F fstab] -afLq | file ...\n"); 869 break; 870 case SWAPCTL: 871 fprintf(stderr, "[-AghklmsU] [-a file ... | -d file ...]\n"); 872 break; 873 } 874 exit(1); 875 } 876 877 static void 878 sizetobuf(char *buf, size_t bufsize, int hflag, long long val, int hlen, 879 long blocksize) 880 { 881 char tmp[16]; 882 883 if (hflag == 'H') { 884 humanize_number(tmp, 5, (int64_t)val, "", HN_AUTOSCALE, 885 HN_B | HN_NOSPACE | HN_DECIMAL); 886 snprintf(buf, bufsize, "%*s", hlen, tmp); 887 } else 888 snprintf(buf, bufsize, "%*lld", hlen, val / blocksize); 889 } 890 891 static void 892 swaplist(int lflag, int sflag, int hflag) 893 { 894 size_t mibsize, size; 895 struct xswdev xsw; 896 int hlen, mib[16], n, pagesize; 897 long blocksize; 898 long long total = 0; 899 long long used = 0; 900 long long tmp_total; 901 long long tmp_used; 902 char buf[32]; 903 904 pagesize = getpagesize(); 905 switch(hflag) { 906 case 'G': 907 blocksize = 1024 * 1024 * 1024; 908 strlcpy(buf, "1GB-blocks", sizeof(buf)); 909 hlen = 10; 910 break; 911 case 'H': 912 blocksize = -1; 913 strlcpy(buf, "Bytes", sizeof(buf)); 914 hlen = 10; 915 break; 916 case 'K': 917 blocksize = 1024; 918 strlcpy(buf, "1kB-blocks", sizeof(buf)); 919 hlen = 10; 920 break; 921 case 'M': 922 blocksize = 1024 * 1024; 923 strlcpy(buf, "1MB-blocks", sizeof(buf)); 924 hlen = 10; 925 break; 926 default: 927 getbsize(&hlen, &blocksize); 928 snprintf(buf, sizeof(buf), "%ld-blocks", blocksize); 929 break; 930 } 931 932 mibsize = nitems(mib); 933 if (sysctlnametomib("vm.swap_info", mib, &mibsize) == -1) 934 err(1, "sysctlnametomib()"); 935 936 if (lflag) { 937 printf("%-13s %*s %*s\n", 938 "Device:", 939 hlen, buf, 940 hlen, "Used:"); 941 } 942 943 for (n = 0; ; ++n) { 944 mib[mibsize] = n; 945 size = sizeof xsw; 946 if (sysctl(mib, mibsize + 1, &xsw, &size, NULL, 0) == -1) 947 break; 948 if (xsw.xsw_version != XSWDEV_VERSION) 949 errx(1, "xswdev version mismatch"); 950 951 tmp_total = (long long)xsw.xsw_nblks * pagesize; 952 tmp_used = (long long)xsw.xsw_used * pagesize; 953 total += tmp_total; 954 used += tmp_used; 955 if (lflag) { 956 sizetobuf(buf, sizeof(buf), hflag, tmp_total, hlen, 957 blocksize); 958 printf("/dev/%-8s %s ", devname(xsw.xsw_dev, S_IFCHR), 959 buf); 960 sizetobuf(buf, sizeof(buf), hflag, tmp_used, hlen, 961 blocksize); 962 printf("%s\n", buf); 963 } 964 } 965 if (errno != ENOENT) 966 err(1, "sysctl()"); 967 968 if (sflag) { 969 sizetobuf(buf, sizeof(buf), hflag, total, hlen, blocksize); 970 printf("Total: %s ", buf); 971 sizetobuf(buf, sizeof(buf), hflag, used, hlen, blocksize); 972 printf("%s\n", buf); 973 } 974 } 975 976