1 /* $NetBSD: swapctl.c,v 1.29 2005/06/12 16:24:20 christos 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> set dumpdev to <dev> 36 * -z show dumpdev 37 * -U remove all devices listed as `sw' in /etc/fstab. 38 * -t [blk|noblk] if -A or -U , add (remove) either all block device 39 * or all non-block devices 40 * -a <dev> add this device 41 * -d <dev> remove this swap device 42 * -g use gigabytes 43 * -h use humanize_number(3) for listing 44 * -l list swap devices 45 * -m use megabytes 46 * -s short listing of swap devices 47 * -k use kilobytes 48 * -p <pri> use this priority 49 * -c change priority 50 * 51 * or, if invoked as "swapon" (compatibility mode): 52 * 53 * -a all devices listed as `sw' in /etc/fstab 54 * -t same as -t above (feature not present in old 55 * swapon(8) command) 56 * <dev> add this device 57 */ 58 #include <sys/cdefs.h> 59 60 #ifndef lint 61 __RCSID("$NetBSD: swapctl.c,v 1.29 2005/06/12 16:24:20 christos Exp $"); 62 #endif 63 64 65 #include <sys/param.h> 66 #include <sys/stat.h> 67 #include <sys/swap.h> 68 69 #include <unistd.h> 70 #include <err.h> 71 #include <errno.h> 72 #include <stdio.h> 73 #include <stdlib.h> 74 #include <string.h> 75 #include <fstab.h> 76 77 #include "swapctl.h" 78 79 int command; 80 81 /* 82 * Commands for swapctl(8). These are mutually exclusive. 83 */ 84 #define CMD_A 0x01 /* process /etc/fstab for adding */ 85 #define CMD_D 0x02 /* set dumpdev */ 86 #define CMD_U 0x04 /* process /etc/fstab for removing */ 87 #define CMD_a 0x08 /* add a swap file/device */ 88 #define CMD_c 0x10 /* change priority of a swap file/device */ 89 #define CMD_d 0x20 /* delete a swap file/device */ 90 #define CMD_l 0x40 /* list swap files/devices */ 91 #define CMD_s 0x80 /* summary of swap files/devices */ 92 #define CMD_z 0x100 /* show dump device */ 93 94 #define SET_COMMAND(cmd) \ 95 do { \ 96 if (command) \ 97 usage(); \ 98 command = (cmd); \ 99 } while (0) 100 101 /* 102 * Commands that require a "path" argument at the end of the command 103 * line, and the ones which require that none exist. 104 */ 105 #define REQUIRE_PATH (CMD_D | CMD_a | CMD_c | CMD_d) 106 #define REQUIRE_NOPATH (CMD_A | CMD_U | CMD_l | CMD_s | CMD_z) 107 108 /* 109 * Option flags, and the commands with which they are valid. 110 */ 111 int kflag; /* display in 1K^x blocks */ 112 #define KFLAG_CMDS (CMD_l | CMD_s) 113 #define MFLAG_CMDS (CMD_l | CMD_s) 114 #define GFLAG_CMDS (CMD_l | CMD_s) 115 116 int hflag; /* display with humanize_number */ 117 #define HFLAG_CMDS (CMD_l | CMD_s) 118 119 int pflag; /* priority was specified */ 120 #define PFLAG_CMDS (CMD_A | CMD_a | CMD_c) 121 122 char *tflag; /* swap device type (blk or noblk) */ 123 #define TFLAG_CMDS (CMD_A | CMD_U) 124 125 int pri; /* uses 0 as default pri */ 126 127 static void change_priority(char *); 128 static int add_swap(char *, int); 129 static int delete_swap(char *); 130 static void set_dumpdev(char *); 131 static void get_dumpdev(void); 132 static void do_fstab(int); 133 static void usage(void); 134 static void swapon_command(int, char **); 135 #if 0 136 static void swapoff_command(int, char **); 137 #endif 138 139 int 140 main(int argc, char *argv[]) 141 { 142 int c; 143 144 if (strcmp(getprogname(), "swapon") == 0) { 145 swapon_command(argc, argv); 146 /* NOTREACHED */ 147 } 148 149 #if 0 150 if (strcmp(getprogname(), "swapoff") == 0) { 151 swapoff_command(argc, argv); 152 /* NOTREACHED */ 153 } 154 #endif 155 156 while ((c = getopt(argc, argv, "ADUacdghklmp:st:z")) != -1) { 157 switch (c) { 158 case 'A': 159 SET_COMMAND(CMD_A); 160 break; 161 162 case 'D': 163 SET_COMMAND(CMD_D); 164 break; 165 166 case 'U': 167 SET_COMMAND(CMD_U); 168 break; 169 170 case 'a': 171 SET_COMMAND(CMD_a); 172 break; 173 174 case 'c': 175 SET_COMMAND(CMD_c); 176 break; 177 178 case 'd': 179 SET_COMMAND(CMD_d); 180 break; 181 182 case 'g': 183 kflag = 3; /* 1k ^ 3 */ 184 break; 185 186 case 'h': 187 hflag = 1; 188 break; 189 190 case 'k': 191 kflag = 1; 192 break; 193 194 case 'l': 195 SET_COMMAND(CMD_l); 196 break; 197 198 case 'm': 199 kflag = 2; /* 1k ^ 2 */ 200 break; 201 202 case 'p': 203 pflag = 1; 204 /* XXX strtol() */ 205 pri = atoi(optarg); 206 break; 207 208 case 's': 209 SET_COMMAND(CMD_s); 210 break; 211 212 case 't': 213 if (tflag != NULL) 214 usage(); 215 tflag = optarg; 216 break; 217 218 case 'z': 219 SET_COMMAND(CMD_z); 220 break; 221 222 default: 223 usage(); 224 /* NOTREACHED */ 225 } 226 } 227 228 /* Did the user specify a command? */ 229 if (command == 0) 230 usage(); 231 232 argv += optind; 233 argc -= optind; 234 235 switch (argc) { 236 case 0: 237 if (command & REQUIRE_PATH) 238 usage(); 239 break; 240 241 case 1: 242 if (command & REQUIRE_NOPATH) 243 usage(); 244 break; 245 246 default: 247 usage(); 248 } 249 250 /* To change priority, you have to specify one. */ 251 if ((command == CMD_c) && pflag == 0) 252 usage(); 253 254 /* Sanity-check -t */ 255 if (tflag != NULL) { 256 if (command != CMD_A && command != CMD_U) 257 usage(); 258 if (strcmp(tflag, "blk") != 0 && 259 strcmp(tflag, "noblk") != 0) 260 usage(); 261 } 262 263 /* Dispatch the command. */ 264 switch (command) { 265 case CMD_l: 266 list_swap(pri, kflag, pflag, 0, 1, hflag); 267 break; 268 269 case CMD_s: 270 list_swap(pri, kflag, pflag, 0, 0, hflag); 271 break; 272 273 case CMD_c: 274 change_priority(argv[0]); 275 break; 276 277 case CMD_a: 278 if (! add_swap(argv[0], pri)) 279 exit(1); 280 break; 281 282 case CMD_d: 283 if (! delete_swap(argv[0])) 284 exit(1); 285 break; 286 287 case CMD_A: 288 do_fstab(1); 289 break; 290 291 case CMD_D: 292 set_dumpdev(argv[0]); 293 break; 294 295 case CMD_z: 296 get_dumpdev(); 297 break; 298 299 case CMD_U: 300 do_fstab(0); 301 break; 302 } 303 304 exit(0); 305 } 306 307 /* 308 * swapon_command: emulate the old swapon(8) program. 309 */ 310 static void 311 swapon_command(int argc, char **argv) 312 { 313 int ch, fiztab = 0; 314 315 while ((ch = getopt(argc, argv, "at:")) != -1) { 316 switch (ch) { 317 case 'a': 318 fiztab = 1; 319 break; 320 case 't': 321 if (tflag != NULL) 322 usage(); 323 tflag = optarg; 324 break; 325 default: 326 goto swapon_usage; 327 } 328 } 329 argc -= optind; 330 argv += optind; 331 332 if (fiztab) { 333 if (argc) 334 goto swapon_usage; 335 /* Sanity-check -t */ 336 if (tflag != NULL) { 337 if (strcmp(tflag, "blk") != 0 && 338 strcmp(tflag, "noblk") != 0) 339 usage(); 340 } 341 do_fstab(1); 342 exit(0); 343 } else if (argc == 0 || tflag != NULL) 344 goto swapon_usage; 345 346 while (argc) { 347 if (! add_swap(argv[0], pri)) 348 exit(1); 349 argc--; 350 argv++; 351 } 352 exit(0); 353 /* NOTREACHED */ 354 355 swapon_usage: 356 fprintf(stderr, "usage: %s -a [-t blk|noblk]\n", getprogname()); 357 fprintf(stderr, " %s <path> ...\n", getprogname()); 358 exit(1); 359 } 360 361 /* 362 * change_priority: change the priority of a swap device. 363 */ 364 static void 365 change_priority(char *path) 366 { 367 368 if (swapctl(SWAP_CTL, path, pri) < 0) 369 warn("%s", path); 370 } 371 372 /* 373 * add_swap: add the pathname to the list of swap devices. 374 */ 375 static int 376 add_swap(char *path, int priority) 377 { 378 struct stat sb; 379 380 if (stat(path, &sb) < 0) 381 goto oops; 382 383 if (sb.st_mode & S_IROTH) 384 warnx("WARNING: %s is readable by the world", path); 385 if (sb.st_mode & S_IWOTH) 386 warnx("WARNING: %s is writable by the world", path); 387 388 if (swapctl(SWAP_ON, path, priority) < 0) { 389 oops: 390 warn("%s", path); 391 return (0); 392 } 393 return (1); 394 } 395 396 /* 397 * delete_swap: remove the pathname to the list of swap devices. 398 */ 399 static int 400 delete_swap(char *path) 401 { 402 403 if (swapctl(SWAP_OFF, path, pri) < 0) { 404 warn("%s", path); 405 return (0); 406 } 407 return (1); 408 } 409 410 static void 411 set_dumpdev(char *path) 412 { 413 414 if (swapctl(SWAP_DUMPDEV, path, 0) == -1) 415 warn("could not set dump device to %s", path); 416 else 417 printf("%s: setting dump device to %s\n", getprogname(), path); 418 } 419 420 static void 421 get_dumpdev(void) 422 { 423 dev_t dev; 424 char *name; 425 426 if (swapctl(SWAP_GETDUMPDEV, &dev, 0) == -1) 427 warn("could not get dump device"); 428 else if (dev == NODEV) 429 printf("no dump device set\n"); 430 else { 431 name = devname(dev, S_IFBLK); 432 printf("dump device is "); 433 if (name) 434 printf("%s\n", name); 435 else 436 printf("major %d minor %d\n", major(dev), minor(dev)); 437 } 438 } 439 440 static void 441 do_fstab(int add) 442 { 443 struct fstab *fp; 444 char *s; 445 long priority; 446 struct stat st; 447 int isblk; 448 int gotone = 0; 449 450 #ifdef RESCUEDIR 451 #define PATH_MOUNT RESCUEDIR "/mount_nfs" 452 #define PATH_UMOUNT RESCUEDIR "/umount" 453 #else 454 #define PATH_MOUNT "/sbin/mount_nfs" 455 #define PATH_UMOUNT "/sbin/umount" 456 #endif 457 458 char cmd[2*PATH_MAX+sizeof(PATH_MOUNT)+2]; 459 460 #define PRIORITYEQ "priority=" 461 #define NFSMNTPT "nfsmntpt=" 462 while ((fp = getfsent()) != NULL) { 463 char *spec; 464 465 spec = fp->fs_spec; 466 cmd[0] = '\0'; 467 468 if (strcmp(fp->fs_type, "dp") == 0 && add) { 469 set_dumpdev(spec); 470 continue; 471 } 472 473 if (strcmp(fp->fs_type, "sw") != 0) 474 continue; 475 476 /* handle dp as mnt option */ 477 if (strstr(fp->fs_mntops, "dp") && add) 478 set_dumpdev(spec); 479 480 isblk = 0; 481 482 if ((s = strstr(fp->fs_mntops, PRIORITYEQ)) != NULL) { 483 s += sizeof(PRIORITYEQ) - 1; 484 priority = atol(s); 485 } else 486 priority = pri; 487 488 if ((s = strstr(fp->fs_mntops, NFSMNTPT)) != NULL) { 489 char *t; 490 491 /* 492 * Skip this song and dance if we're only 493 * doing block devices. 494 */ 495 if (tflag != NULL && strcmp(tflag, "blk") == 0) 496 continue; 497 498 t = strpbrk(s, ","); 499 if (t != 0) 500 *t = '\0'; 501 spec = strdup(s + strlen(NFSMNTPT)); 502 if (t != 0) 503 *t = ','; 504 505 if (spec == NULL) 506 errx(1, "Out of memory"); 507 508 if (strlen(spec) == 0) { 509 warnx("empty mountpoint"); 510 free(spec); 511 continue; 512 } 513 if (add) { 514 snprintf(cmd, sizeof(cmd), "%s %s %s", 515 PATH_MOUNT, fp->fs_spec, spec); 516 if (system(cmd) != 0) { 517 warnx("%s: mount failed", fp->fs_spec); 518 continue; 519 } 520 } else { 521 snprintf(cmd, sizeof(cmd), "%s %s", 522 PATH_UMOUNT, fp->fs_spec); 523 } 524 } else { 525 /* 526 * Determine blk-ness. 527 */ 528 if (stat(spec, &st) < 0) { 529 warn("%s", spec); 530 continue; 531 } 532 if (S_ISBLK(st.st_mode)) 533 isblk = 1; 534 } 535 536 /* 537 * Skip this type if we're told to. 538 */ 539 if (tflag != NULL) { 540 if (strcmp(tflag, "blk") == 0 && isblk == 0) 541 continue; 542 if (strcmp(tflag, "noblk") == 0 && isblk == 1) 543 continue; 544 } 545 546 if (add) { 547 if (add_swap(spec, (int)priority)) { 548 gotone = 1; 549 printf( 550 "%s: adding %s as swap device at priority %d\n", 551 getprogname(), fp->fs_spec, (int)priority); 552 } 553 } else { 554 if (delete_swap(spec)) { 555 gotone = 1; 556 printf( 557 "%s: removing %s as swap device\n", 558 getprogname(), fp->fs_spec); 559 } 560 if (cmd[0]) { 561 if (system(cmd) != 0) { 562 warnx("%s: umount failed", fp->fs_spec); 563 continue; 564 } 565 } 566 } 567 568 if (spec != fp->fs_spec) 569 free(spec); 570 } 571 if (gotone == 0) 572 exit(1); 573 } 574 575 static void 576 usage(void) 577 { 578 const char *progname = getprogname(); 579 580 fprintf(stderr, "usage: %s -A [-p priority] [-t blk|noblk]\n", 581 progname); 582 fprintf(stderr, " %s -D dumppath\n", progname); 583 fprintf(stderr, " %s -U [-t blk|noblk]\n", progname); 584 fprintf(stderr, " %s -a [-p priority] path\n", progname); 585 fprintf(stderr, " %s -c -p priority path\n", progname); 586 fprintf(stderr, " %s -d path\n", progname); 587 fprintf(stderr, " %s -l | -s [-k|-m|-g|-h]\n", progname); 588 fprintf(stderr, " %s -z\n", progname); 589 exit(1); 590 } 591