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