1/* $NetBSD: swapctl.c,v 1.35 2009/09/24 16:15:20 apb Exp $ */ 2 3/* 4 * Copyright (c) 1996, 1997, 1999 Matthew R. Green 5 * 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 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 21 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 23 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 */ 28 29/* 30 * swapctl command: 31 * -A add all devices listed as `sw' in /etc/fstab (also 32 * (sets the dump device, if listed in fstab) 33 * -D [<dev>|none] set dumpdev to <dev> or disable dumps 34 * -z show dumpdev 35 * -U remove all devices listed as `sw' in /etc/fstab. 36 * -t [blk|noblk|auto] 37 * if -A or -U , add (remove) either all block device 38 * or all non-block devices, or all swap partitions 39 * -q check if any swap or dump devices are defined in 40 * /etc/fstab 41 * -a <dev> add this device 42 * -d <dev> remove this swap device 43 * -f with -A -t auto, use the first swap as dump device 44 * -g use gigabytes 45 * -h use humanize_number(3) for listing 46 * -l list swap devices 47 * -m use megabytes 48 * -n print actions, but do not add/remove swap or 49 * with -A/-U 50 * -o with -A -t auto only configure the first swap as dump, 51 * (similar to -f), but do not add any further swap devs 52 * -s short listing of swap devices 53 * -k use kilobytes 54 * -p <pri> use this priority 55 * -c change priority 56 * 57 * or, if invoked as "swapon" (compatibility mode): 58 * 59 * -a all devices listed as `sw' in /etc/fstab 60 * -t same as -t above (feature not present in old 61 * swapon(8) command) 62 * <dev> add this device 63 */ 64#include <sys/cdefs.h> 65 66#ifndef lint 67__RCSID("$NetBSD: swapctl.c,v 1.35 2009/09/24 16:15:20 apb Exp $"); 68#endif 69 70 71#include <sys/param.h> 72#include <sys/stat.h> 73#include <sys/swap.h> 74#include <sys/sysctl.h> 75#include <sys/disk.h> 76#include <sys/disklabel.h> 77 78#include <unistd.h> 79#include <err.h> 80#include <errno.h> 81#include <stdio.h> 82#include <stdlib.h> 83#include <string.h> 84#include <fstab.h> 85#include <fcntl.h> 86#include <util.h> 87#include <paths.h> 88 89#include "swapctl.h" 90 91static int command; 92 93/* 94 * Commands for swapctl(8). These are mutually exclusive. 95 */ 96#define CMD_A 0x01 /* process /etc/fstab for adding */ 97#define CMD_D 0x02 /* set dumpdev */ 98#define CMD_U 0x04 /* process /etc/fstab for removing */ 99#define CMD_a 0x08 /* add a swap file/device */ 100#define CMD_c 0x10 /* change priority of a swap file/device */ 101#define CMD_d 0x20 /* delete a swap file/device */ 102#define CMD_l 0x40 /* list swap files/devices */ 103#define CMD_s 0x80 /* summary of swap files/devices */ 104#define CMD_z 0x100 /* show dump device */ 105#define CMD_q 0x200 /* check for dump/swap in /etc/fstab */ 106 107#define SET_COMMAND(cmd) \ 108do { \ 109 if (command) \ 110 usage(); \ 111 command = (cmd); \ 112} while (0) 113 114/* 115 * Commands that require a "path" argument at the end of the command 116 * line, and the ones which require that none exist. 117 */ 118#define REQUIRE_PATH (CMD_D | CMD_a | CMD_c | CMD_d) 119#define REQUIRE_NOPATH (CMD_A | CMD_U | CMD_l | CMD_s | CMD_z | CMD_q) 120 121/* 122 * Option flags, and the commands with which they are valid. 123 */ 124static int kflag; /* display in 1K^x blocks */ 125#define KFLAG_CMDS (CMD_l | CMD_s) 126#define MFLAG_CMDS (CMD_l | CMD_s) 127#define GFLAG_CMDS (CMD_l | CMD_s) 128 129static int hflag; /* display with humanize_number */ 130#define HFLAG_CMDS (CMD_l | CMD_s) 131 132static int pflag; /* priority was specified */ 133#define PFLAG_CMDS (CMD_A | CMD_a | CMD_c) 134 135static char *tflag; /* swap device type (blk, noblk, auto) */ 136static int autoflag; /* 1, if tflag is "auto" */ 137#define TFLAG_CMDS (CMD_A | CMD_U) 138 139static int fflag; /* first swap becomes dump */ 140#define FFLAG_CMDS (CMD_A) 141 142static int oflag; /* only autoset dump device */ 143#define OFLAG_CMDS (CMD_A) 144 145static int nflag; /* no execute, just print actions */ 146#define NFLAG_CMDS (CMD_A | CMD_U) 147 148static int pri; /* uses 0 as default pri */ 149 150static void change_priority(char *); 151static int add_swap(char *, int); 152static int delete_swap(char *); 153static void set_dumpdev(char *); 154static int get_dumpdev(void); 155__dead static void do_fstab(int); 156static int check_fstab(void); 157static void do_localdevs(int); 158static void do_localdisk(const char *, int); 159static int do_wedgesofdisk(int fd, int); 160static int do_partitionsofdisk(const char *, int fd, int); 161__dead static void usage(void); 162__dead static void swapon_command(int, char **); 163#if 0 164static void swapoff_command(int, char **); 165#endif 166 167int 168main(int argc, char *argv[]) 169{ 170 int c; 171 172 if (strcmp(getprogname(), "swapon") == 0) { 173 swapon_command(argc, argv); 174 /* NOTREACHED */ 175 } 176 177#if 0 178 if (strcmp(getprogname(), "swapoff") == 0) { 179 swapoff_command(argc, argv); 180 /* NOTREACHED */ 181 } 182#endif 183 184 while ((c = getopt(argc, argv, "ADUacdfghklmnop:qst:z")) != -1) { 185 switch (c) { 186 case 'A': 187 SET_COMMAND(CMD_A); 188 break; 189 190 case 'D': 191 SET_COMMAND(CMD_D); 192 break; 193 194 case 'U': 195 SET_COMMAND(CMD_U); 196 break; 197 198 case 'a': 199 SET_COMMAND(CMD_a); 200 break; 201 202 case 'c': 203 SET_COMMAND(CMD_c); 204 break; 205 206 case 'd': 207 SET_COMMAND(CMD_d); 208 break; 209 210 case 'f': 211 fflag = 1; 212 break; 213 214 case 'g': 215 kflag = 3; /* 1k ^ 3 */ 216 break; 217 218 case 'h': 219 hflag = 1; 220 break; 221 222 case 'k': 223 kflag = 1; 224 break; 225 226 case 'l': 227 SET_COMMAND(CMD_l); 228 break; 229 230 case 'm': 231 kflag = 2; /* 1k ^ 2 */ 232 break; 233 234 case 'n': 235 nflag = 1; 236 break; 237 238 case 'o': 239 oflag = 1; 240 break; 241 242 case 'p': 243 pflag = 1; 244 /* XXX strtol() */ 245 pri = atoi(optarg); 246 break; 247 248 case 'q': 249 SET_COMMAND(CMD_q); 250 break; 251 252 case 's': 253 SET_COMMAND(CMD_s); 254 break; 255 256 case 't': 257 if (tflag != NULL) 258 usage(); 259 tflag = optarg; 260 if (strcmp(tflag, "auto") == 0) 261 autoflag = 1; 262 break; 263 264 case 'z': 265 SET_COMMAND(CMD_z); 266 break; 267 268 default: 269 usage(); 270 /* NOTREACHED */ 271 } 272 } 273 274 /* Did the user specify a command? */ 275 if (command == 0) 276 usage(); 277 278 argv += optind; 279 argc -= optind; 280 281 switch (argc) { 282 case 0: 283 if (command & REQUIRE_PATH) 284 usage(); 285 break; 286 287 case 1: 288 if (command & REQUIRE_NOPATH) 289 usage(); 290 break; 291 292 default: 293 usage(); 294 } 295 296 /* To change priority, you have to specify one. */ 297 if ((command == CMD_c) && pflag == 0) 298 usage(); 299 300 /* -f and -o are mutualy exclusive */ 301 if (fflag && oflag) 302 usage(); 303 304 /* Sanity-check -t */ 305 if (tflag != NULL) { 306 if (command != CMD_A && command != CMD_U) 307 usage(); 308 if (strcmp(tflag, "blk") != 0 && 309 strcmp(tflag, "noblk") != 0 && 310 strcmp(tflag, "auto") != 0) 311 usage(); 312 } 313 314 /* Dispatch the command. */ 315 switch (command) { 316 case CMD_l: 317 if (!list_swap(pri, kflag, pflag, 0, 1, hflag)) 318 exit(1); 319 break; 320 321 case CMD_s: 322 list_swap(pri, kflag, pflag, 0, 0, hflag); 323 break; 324 325 case CMD_c: 326 change_priority(argv[0]); 327 break; 328 329 case CMD_a: 330 if (! add_swap(argv[0], pri)) 331 exit(1); 332 break; 333 334 case CMD_d: 335 if (! delete_swap(argv[0])) 336 exit(1); 337 break; 338 339 case CMD_A: 340 if (autoflag) 341 do_localdevs(1); 342 else 343 do_fstab(1); 344 break; 345 346 case CMD_D: 347 set_dumpdev(argv[0]); 348 break; 349 350 case CMD_z: 351 if (!get_dumpdev()) 352 exit(1); 353 break; 354 355 case CMD_U: 356 if (autoflag) 357 do_localdevs(0); 358 else 359 do_fstab(0); 360 break; 361 case CMD_q: 362 if (check_fstab()) { 363 printf("%s: there are swap or dump devices defined in " 364 _PATH_FSTAB "\n", getprogname()); 365 exit(0); 366 } else { 367 printf("%s: no swap or dump devices in " 368 _PATH_FSTAB "\n", getprogname()); 369 exit(1); 370 } 371 } 372 373 exit(0); 374} 375 376/* 377 * swapon_command: emulate the old swapon(8) program. 378 */ 379static void 380swapon_command(int argc, char **argv) 381{ 382 int ch, fiztab = 0; 383 384 while ((ch = getopt(argc, argv, "at:")) != -1) { 385 switch (ch) { 386 case 'a': 387 fiztab = 1; 388 break; 389 case 't': 390 if (tflag != NULL) 391 usage(); 392 tflag = optarg; 393 break; 394 default: 395 goto swapon_usage; 396 } 397 } 398 argc -= optind; 399 argv += optind; 400 401 if (fiztab) { 402 if (argc) 403 goto swapon_usage; 404 /* Sanity-check -t */ 405 if (tflag != NULL) { 406 if (strcmp(tflag, "blk") != 0 && 407 strcmp(tflag, "noblk") != 0) 408 usage(); 409 } 410 do_fstab(1); 411 exit(0); 412 } else if (argc == 0 || tflag != NULL) 413 goto swapon_usage; 414 415 while (argc) { 416 if (! add_swap(argv[0], pri)) 417 exit(1); 418 argc--; 419 argv++; 420 } 421 exit(0); 422 /* NOTREACHED */ 423 424 swapon_usage: 425 fprintf(stderr, "usage: %s -a [-t blk|noblk]\n", getprogname()); 426 fprintf(stderr, " %s <path> ...\n", getprogname()); 427 exit(1); 428} 429 430/* 431 * change_priority: change the priority of a swap device. 432 */ 433static void 434change_priority(char *path) 435{ 436 437 if (swapctl(SWAP_CTL, path, pri) < 0) 438 err(1, "%s", path); 439} 440 441/* 442 * add_swap: add the pathname to the list of swap devices. 443 */ 444static int 445add_swap(char *path, int priority) 446{ 447 struct stat sb; 448 449 if (stat(path, &sb) < 0) 450 goto oops; 451 452 if (sb.st_mode & S_IROTH) 453 warnx("WARNING: %s is readable by the world", path); 454 if (sb.st_mode & S_IWOTH) 455 warnx("WARNING: %s is writable by the world", path); 456 457 if (fflag || oflag) { 458 set_dumpdev(path); 459 if (oflag) 460 exit(0); 461 else 462 fflag = 0; 463 } 464 465 if (nflag) 466 return 1; 467 468 if (swapctl(SWAP_ON, path, priority) < 0) { 469oops: 470 err(1, "%s", path); 471 } 472 return (1); 473} 474 475/* 476 * delete_swap: remove the pathname to the list of swap devices. 477 */ 478static int 479delete_swap(char *path) 480{ 481 482 if (nflag) 483 return 1; 484 485 if (swapctl(SWAP_OFF, path, pri) < 0) 486 err(1, "%s", path); 487 return (1); 488} 489 490static void 491set_dumpdev(char *path) 492{ 493 int rv = 0; 494 495 if (!nflag) { 496 if (strcmp(path, "none") == 0) 497 rv = swapctl(SWAP_DUMPOFF, NULL, 0); 498 else 499 rv = swapctl(SWAP_DUMPDEV, path, 0); 500 } 501 502 if (rv == -1) 503 err(1, "could not set dump device to %s", path); 504 else 505 printf("%s: setting dump device to %s\n", getprogname(), path); 506} 507 508static int 509get_dumpdev(void) 510{ 511 dev_t dev; 512 char *name; 513 514 if (swapctl(SWAP_GETDUMPDEV, &dev, 0) == -1) { 515 warn("could not get dump device"); 516 return 0; 517 } else if (dev == NODEV) { 518 printf("no dump device set\n"); 519 return 0; 520 } else { 521 name = devname(dev, S_IFBLK); 522 printf("dump device is "); 523 if (name) 524 printf("%s\n", name); 525 else 526 printf("major %llu minor %llu\n", 527 (unsigned long long)major(dev), 528 (unsigned long long)minor(dev)); 529 } 530 return 1; 531} 532 533static void 534do_localdevs(int add) 535{ 536 size_t ressize; 537 char *disknames, *disk; 538 static const char mibname[] = "hw.disknames"; 539 540 ressize = 0; 541 if (sysctlbyname(mibname, NULL, &ressize, NULL, 0)) 542 return; 543 ressize += 200; /* add some arbitrary slope */ 544 disknames = malloc(ressize); 545 if (sysctlbyname(mibname, disknames, &ressize, NULL, 0) == 0) { 546 for (disk = strtok(disknames, " "); disk; 547 disk = strtok(NULL, " ")) 548 do_localdisk(disk, add); 549 } 550 free(disknames); 551} 552 553static void 554do_localdisk(const char *disk, int add) 555{ 556 int fd; 557 char dvname[MAXPATHLEN]; 558 559 if ((fd = opendisk(disk, O_RDONLY, dvname, sizeof(dvname), 0)) == -1) 560 return; 561 562 if (!do_wedgesofdisk(fd, add)) 563 do_partitionsofdisk(disk, fd, add); 564 565 close(fd); 566} 567 568static int 569do_wedgesofdisk(int fd, int add) 570{ 571 char devicename[MAXPATHLEN]; 572 struct dkwedge_info *dkw; 573 struct dkwedge_list dkwl; 574 size_t bufsize; 575 u_int i; 576 577 dkw = NULL; 578 dkwl.dkwl_buf = dkw; 579 dkwl.dkwl_bufsize = 0; 580 581 for (;;) { 582 if (ioctl(fd, DIOCLWEDGES, &dkwl) == -1) 583 return 0; 584 if (dkwl.dkwl_nwedges == dkwl.dkwl_ncopied) 585 break; 586 bufsize = dkwl.dkwl_nwedges * sizeof(*dkw); 587 if (dkwl.dkwl_bufsize < bufsize) { 588 dkw = realloc(dkwl.dkwl_buf, bufsize); 589 if (dkw == NULL) 590 return 0; 591 dkwl.dkwl_buf = dkw; 592 dkwl.dkwl_bufsize = bufsize; 593 } 594 } 595 596 for (i = 0; i < dkwl.dkwl_ncopied; i++) { 597 if (strcmp(dkw[i].dkw_ptype, DKW_PTYPE_SWAP) != 0) 598 continue; 599 snprintf(devicename, sizeof(devicename), "%s%s", _PATH_DEV, 600 dkw[i].dkw_devname); 601 devicename[sizeof(devicename)-1] = '\0'; 602 603 if (add) { 604 if (add_swap(devicename, pri)) { 605 printf( 606 "%s: adding %s as swap device at priority 0\n", 607 getprogname(), devicename); 608 } 609 } else { 610 if (delete_swap(devicename)) { 611 printf( 612 "%s: removing %s as swap device\n", 613 getprogname(), devicename); 614 } 615 } 616 617 } 618 619 free(dkw); 620 return dkwl.dkwl_nwedges != 0; 621} 622 623static int 624do_partitionsofdisk(const char *prefix, int fd, int add) 625{ 626 char devicename[MAXPATHLEN]; 627 struct disklabel lab; 628 uint i; 629 630 if (ioctl(fd, DIOCGDINFO, &lab) != 0) 631 return 0; 632 633 for (i = 0; i < lab.d_npartitions; i++) { 634 if (lab.d_partitions[i].p_fstype != FS_SWAP) 635 continue; 636 snprintf(devicename, sizeof(devicename), "%s%s%c", _PATH_DEV, 637 prefix, 'a'+i); 638 devicename[sizeof(devicename)-1] = '\0'; 639 640 if (add) { 641 if (add_swap(devicename, pri)) { 642 printf( 643 "%s: adding %s as swap device at priority 0\n", 644 getprogname(), devicename); 645 } 646 } else { 647 if (delete_swap(devicename)) { 648 printf( 649 "%s: removing %s as swap device\n", 650 getprogname(), devicename); 651 } 652 } 653 } 654 655 return 1; 656} 657 658static int 659check_fstab(void) 660{ 661 struct fstab *fp; 662 663 while ((fp = getfsent()) != NULL) { 664 if (strcmp(fp->fs_type, "dp") == 0) 665 return 1; 666 667 if (strcmp(fp->fs_type, "sw") == 0) 668 return 1; 669 } 670 671 return 0; 672} 673 674static void 675do_fstab(int add) 676{ 677 struct fstab *fp; 678 char *s; 679 long priority; 680 struct stat st; 681 int isblk; 682 int success = 0; /* set to 1 after a successful operation */ 683 int error = 0; /* set to 1 after an error */ 684 685#ifdef RESCUEDIR 686#define PATH_MOUNT RESCUEDIR "/mount_nfs" 687#define PATH_UMOUNT RESCUEDIR "/umount" 688#else 689#define PATH_MOUNT "/sbin/mount_nfs" 690#define PATH_UMOUNT "/sbin/umount" 691#endif 692 693 char cmd[2*PATH_MAX+sizeof(PATH_MOUNT)+2]; 694 695#define PRIORITYEQ "priority=" 696#define NFSMNTPT "nfsmntpt=" 697 while ((fp = getfsent()) != NULL) { 698 char *spec; 699 700 spec = fp->fs_spec; 701 cmd[0] = '\0'; 702 703 if (strcmp(fp->fs_type, "dp") == 0 && add) { 704 set_dumpdev(spec); 705 continue; 706 } 707 708 if (strcmp(fp->fs_type, "sw") != 0) 709 continue; 710 711 /* handle dp as mnt option */ 712 if (strstr(fp->fs_mntops, "dp") && add) 713 set_dumpdev(spec); 714 715 isblk = 0; 716 717 if ((s = strstr(fp->fs_mntops, PRIORITYEQ)) != NULL) { 718 s += sizeof(PRIORITYEQ) - 1; 719 priority = atol(s); 720 } else 721 priority = pri; 722 723 if ((s = strstr(fp->fs_mntops, NFSMNTPT)) != NULL) { 724 char *t; 725 726 /* 727 * Skip this song and dance if we're only 728 * doing block devices. 729 */ 730 if (tflag != NULL && strcmp(tflag, "blk") == 0) 731 continue; 732 733 t = strpbrk(s, ","); 734 if (t != 0) 735 *t = '\0'; 736 spec = strdup(s + strlen(NFSMNTPT)); 737 if (t != 0) 738 *t = ','; 739 740 if (spec == NULL) 741 errx(1, "Out of memory"); 742 743 if (strlen(spec) == 0) { 744 warnx("empty mountpoint"); 745 free(spec); 746 continue; 747 } 748 if (add) { 749 snprintf(cmd, sizeof(cmd), "%s %s %s", 750 PATH_MOUNT, fp->fs_spec, spec); 751 if (system(cmd) != 0) { 752 warnx("%s: mount failed", fp->fs_spec); 753 continue; 754 } 755 } else { 756 snprintf(cmd, sizeof(cmd), "%s %s", 757 PATH_UMOUNT, fp->fs_spec); 758 } 759 } else { 760 /* 761 * Determine blk-ness. 762 */ 763 if (stat(spec, &st) < 0) { 764 warn("%s", spec); 765 continue; 766 } 767 if (S_ISBLK(st.st_mode)) 768 isblk = 1; 769 } 770 771 /* 772 * Skip this type if we're told to. 773 */ 774 if (tflag != NULL) { 775 if (strcmp(tflag, "blk") == 0 && isblk == 0) 776 continue; 777 if (strcmp(tflag, "noblk") == 0 && isblk == 1) 778 continue; 779 } 780 781 if (add) { 782 if (add_swap(spec, (int)priority)) { 783 success = 1; 784 printf( 785 "%s: adding %s as swap device at priority %d\n", 786 getprogname(), fp->fs_spec, (int)priority); 787 } else { 788 error = 1; 789 fprintf(stderr, 790 "%s: failed to add %s as swap device\n", 791 getprogname(), fp->fs_spec); 792 } 793 } else { 794 if (delete_swap(spec)) { 795 success = 1; 796 printf( 797 "%s: removing %s as swap device\n", 798 getprogname(), fp->fs_spec); 799 } else { 800 error = 1; 801 fprintf(stderr, 802 "%s: failed to remove %s as swap device\n", 803 getprogname(), fp->fs_spec); 804 } 805 if (cmd[0]) { 806 if (system(cmd) != 0) { 807 warnx("%s: umount failed", fp->fs_spec); 808 error = 1; 809 continue; 810 } 811 } 812 } 813 814 if (spec != fp->fs_spec) 815 free(spec); 816 } 817 if (error) 818 exit(1); 819 else if (success) 820 exit(0); 821 else 822 exit(2); /* not really an error, but no swap devices found */ 823} 824 825static void 826usage(void) 827{ 828 const char *progname = getprogname(); 829 830 fprintf(stderr, "usage: %s -A [-f|-o] [-n] [-p priority] " 831 "[-t blk|noblk|auto]\n", progname); 832 fprintf(stderr, " %s -a [-p priority] path\n", progname); 833 fprintf(stderr, " %s -q\n", progname); 834 fprintf(stderr, " %s -c -p priority path\n", progname); 835 fprintf(stderr, " %s -D dumpdev|none\n", progname); 836 fprintf(stderr, " %s -d path\n", progname); 837 fprintf(stderr, " %s -l | -s [-k|-m|-g|-h]\n", progname); 838 fprintf(stderr, " %s -U [-n] [-t blk|noblk|auto]\n", progname); 839 fprintf(stderr, " %s -z\n", progname); 840 exit(1); 841} 842