1 /* $NetBSD: swapctl.c,v 1.26 2004/06/08 08:13:16 cjep 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.26 2004/06/08 08:13:16 cjep 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 __P((const char *)); 128 static int add_swap __P((const char *, int)); 129 static int delete_swap __P((const char *)); 130 static void set_dumpdev __P((const char *)); 131 static void get_dumpdev __P((void)); 132 int main __P((int, char *[])); 133 static void do_fstab __P((int)); 134 static void usage __P((void)); 135 static void swapon_command __P((int, char **)); 136 #if 0 137 static void swapoff_command __P((int, char **)); 138 #endif 139 140 int 141 main(argc, argv) 142 int argc; 143 char *argv[]; 144 { 145 int c; 146 147 if (strcmp(getprogname(), "swapon") == 0) { 148 swapon_command(argc, argv); 149 /* NOTREACHED */ 150 } 151 152 #if 0 153 if (strcmp(getprogname(), "swapoff") == 0) { 154 swapoff_command(argc, argv); 155 /* NOTREACHED */ 156 } 157 #endif 158 159 while ((c = getopt(argc, argv, "ADUacdghklmp:st:z")) != -1) { 160 switch (c) { 161 case 'A': 162 SET_COMMAND(CMD_A); 163 break; 164 165 case 'D': 166 SET_COMMAND(CMD_D); 167 break; 168 169 case 'U': 170 SET_COMMAND(CMD_U); 171 break; 172 173 case 'a': 174 SET_COMMAND(CMD_a); 175 break; 176 177 case 'c': 178 SET_COMMAND(CMD_c); 179 break; 180 181 case 'd': 182 SET_COMMAND(CMD_d); 183 break; 184 185 case 'g': 186 kflag = 3; /* 1k ^ 3 */ 187 break; 188 189 case 'h': 190 hflag = 1; 191 break; 192 193 case 'k': 194 kflag = 1; 195 break; 196 197 case 'l': 198 SET_COMMAND(CMD_l); 199 break; 200 201 case 'm': 202 kflag = 2; /* 1k ^ 2 */ 203 break; 204 205 case 'p': 206 pflag = 1; 207 /* XXX strtol() */ 208 pri = atoi(optarg); 209 break; 210 211 case 's': 212 SET_COMMAND(CMD_s); 213 break; 214 215 case 't': 216 if (tflag != NULL) 217 usage(); 218 tflag = optarg; 219 break; 220 221 case 'z': 222 SET_COMMAND(CMD_z); 223 break; 224 225 default: 226 usage(); 227 /* NOTREACHED */ 228 } 229 } 230 231 /* Did the user specify a command? */ 232 if (command == 0) 233 usage(); 234 235 argv += optind; 236 argc -= optind; 237 238 switch (argc) { 239 case 0: 240 if (command & REQUIRE_PATH) 241 usage(); 242 break; 243 244 case 1: 245 if (command & REQUIRE_NOPATH) 246 usage(); 247 break; 248 249 default: 250 usage(); 251 } 252 253 /* To change priority, you have to specify one. */ 254 if ((command == CMD_c) && pflag == 0) 255 usage(); 256 257 /* Sanity-check -t */ 258 if (tflag != NULL) { 259 if (command != CMD_A && command != CMD_U) 260 usage(); 261 if (strcmp(tflag, "blk") != 0 && 262 strcmp(tflag, "noblk") != 0) 263 usage(); 264 } 265 266 /* Dispatch the command. */ 267 switch (command) { 268 case CMD_l: 269 list_swap(pri, kflag, pflag, 0, 1, hflag); 270 break; 271 272 case CMD_s: 273 list_swap(pri, kflag, pflag, 0, 0, hflag); 274 break; 275 276 case CMD_c: 277 change_priority(argv[0]); 278 break; 279 280 case CMD_a: 281 if (! add_swap(argv[0], pri)) 282 exit(1); 283 break; 284 285 case CMD_d: 286 if (! delete_swap(argv[0])) 287 exit(1); 288 break; 289 290 case CMD_A: 291 do_fstab(1); 292 break; 293 294 case CMD_D: 295 set_dumpdev(argv[0]); 296 break; 297 298 case CMD_z: 299 get_dumpdev(); 300 break; 301 302 case CMD_U: 303 do_fstab(0); 304 break; 305 } 306 307 exit(0); 308 } 309 310 /* 311 * swapon_command: emulate the old swapon(8) program. 312 */ 313 static void 314 swapon_command(argc, argv) 315 int argc; 316 char **argv; 317 { 318 int ch, fiztab = 0; 319 320 while ((ch = getopt(argc, argv, "at:")) != -1) { 321 switch (ch) { 322 case 'a': 323 fiztab = 1; 324 break; 325 case 't': 326 if (tflag != NULL) 327 usage(); 328 tflag = optarg; 329 break; 330 default: 331 goto swapon_usage; 332 } 333 } 334 argc -= optind; 335 argv += optind; 336 337 if (fiztab) { 338 if (argc) 339 goto swapon_usage; 340 /* Sanity-check -t */ 341 if (tflag != NULL) { 342 if (strcmp(tflag, "blk") != 0 && 343 strcmp(tflag, "noblk") != 0) 344 usage(); 345 } 346 do_fstab(1); 347 exit(0); 348 } else if (argc == 0 || tflag != NULL) 349 goto swapon_usage; 350 351 while (argc) { 352 if (! add_swap(argv[0], pri)) 353 exit(1); 354 argc--; 355 argv++; 356 } 357 exit(0); 358 /* NOTREACHED */ 359 360 swapon_usage: 361 fprintf(stderr, "usage: %s -a [-t blk|noblk]\n", getprogname()); 362 fprintf(stderr, " %s <path> ...\n", getprogname()); 363 exit(1); 364 } 365 366 /* 367 * change_priority: change the priority of a swap device. 368 */ 369 static void 370 change_priority(path) 371 const char *path; 372 { 373 374 if (swapctl(SWAP_CTL, path, pri) < 0) 375 warn("%s", path); 376 } 377 378 /* 379 * add_swap: add the pathname to the list of swap devices. 380 */ 381 static int 382 add_swap(path, priority) 383 const char *path; 384 int priority; 385 { 386 struct stat sb; 387 388 if (stat(path, &sb) < 0) 389 goto oops; 390 391 if (sb.st_mode & S_IROTH) 392 warnx("WARNING: %s is readable by the world", path); 393 if (sb.st_mode & S_IWOTH) 394 warnx("WARNING: %s is writable by the world", path); 395 396 if (swapctl(SWAP_ON, path, priority) < 0) { 397 oops: 398 warn("%s", path); 399 return (0); 400 } 401 return (1); 402 } 403 404 /* 405 * delete_swap: remove the pathname to the list of swap devices. 406 */ 407 static int 408 delete_swap(path) 409 const char *path; 410 { 411 412 if (swapctl(SWAP_OFF, path, pri) < 0) { 413 warn("%s", path); 414 return (0); 415 } 416 return (1); 417 } 418 419 static void 420 set_dumpdev(path) 421 const char *path; 422 { 423 424 if (swapctl(SWAP_DUMPDEV, path, 0) == -1) 425 warn("could not set dump device to %s", path); 426 else 427 printf("%s: setting dump device to %s\n", getprogname(), path); 428 } 429 430 static void 431 get_dumpdev() 432 { 433 dev_t dev; 434 char *name; 435 436 if (swapctl(SWAP_GETDUMPDEV, &dev, 0) == -1) 437 warn("could not get dump device"); 438 else if (dev == NODEV) 439 printf("no dump device set\n"); 440 else { 441 name = devname(dev, S_IFBLK); 442 printf("dump device is "); 443 if (name) 444 printf("%s\n", name); 445 else 446 printf("major %d minor %d\n", major(dev), minor(dev)); 447 } 448 } 449 450 static void 451 do_fstab(add) 452 int add; 453 { 454 struct fstab *fp; 455 char *s; 456 long priority; 457 struct stat st; 458 int isblk; 459 int gotone = 0; 460 #define PATH_MOUNT "/sbin/mount_nfs" 461 #define PATH_UMOUNT "/sbin/umount" 462 char cmd[2*PATH_MAX+sizeof(PATH_MOUNT)+2]; 463 464 #define PRIORITYEQ "priority=" 465 #define NFSMNTPT "nfsmntpt=" 466 while ((fp = getfsent()) != NULL) { 467 const char *spec; 468 469 spec = fp->fs_spec; 470 cmd[0] = '\0'; 471 472 if (strcmp(fp->fs_type, "dp") == 0 && add) { 473 set_dumpdev(spec); 474 continue; 475 } 476 477 if (strcmp(fp->fs_type, "sw") != 0) 478 continue; 479 480 /* handle dp as mnt option */ 481 if (strstr(fp->fs_mntops, "dp") && add) 482 set_dumpdev(spec); 483 484 isblk = 0; 485 486 if ((s = strstr(fp->fs_mntops, PRIORITYEQ)) != NULL) { 487 s += sizeof(PRIORITYEQ) - 1; 488 priority = atol(s); 489 } else 490 priority = pri; 491 492 if ((s = strstr(fp->fs_mntops, NFSMNTPT)) != NULL) { 493 char *t; 494 495 /* 496 * Skip this song and dance if we're only 497 * doing block devices. 498 */ 499 if (tflag != NULL && strcmp(tflag, "blk") == 0) 500 continue; 501 502 t = strpbrk(s, ","); 503 if (t != 0) 504 *t = '\0'; 505 spec = strdup(s + strlen(NFSMNTPT)); 506 if (t != 0) 507 *t = ','; 508 509 if (spec == NULL) 510 errx(1, "Out of memory"); 511 512 if (strlen(spec) == 0) { 513 warnx("empty mountpoint"); 514 free((char *)spec); 515 continue; 516 } 517 if (add) { 518 snprintf(cmd, sizeof(cmd), "%s %s %s", 519 PATH_MOUNT, fp->fs_spec, spec); 520 if (system(cmd) != 0) { 521 warnx("%s: mount failed", fp->fs_spec); 522 continue; 523 } 524 } else { 525 snprintf(cmd, sizeof(cmd), "%s %s", 526 PATH_UMOUNT, fp->fs_spec); 527 } 528 } else { 529 /* 530 * Determine blk-ness. 531 */ 532 if (stat(spec, &st) < 0) { 533 warn("%s", spec); 534 continue; 535 } 536 if (S_ISBLK(st.st_mode)) 537 isblk = 1; 538 } 539 540 /* 541 * Skip this type if we're told to. 542 */ 543 if (tflag != NULL) { 544 if (strcmp(tflag, "blk") == 0 && isblk == 0) 545 continue; 546 if (strcmp(tflag, "noblk") == 0 && isblk == 1) 547 continue; 548 } 549 550 if (add) { 551 if (add_swap(spec, (int)priority)) { 552 gotone = 1; 553 printf( 554 "%s: adding %s as swap device at priority %d\n", 555 getprogname(), fp->fs_spec, (int)priority); 556 } 557 } else { 558 if (delete_swap(spec)) { 559 gotone = 1; 560 printf( 561 "%s: removing %s as swap device\n", 562 getprogname(), fp->fs_spec); 563 } 564 if (cmd[0]) { 565 if (system(cmd) != 0) { 566 warnx("%s: umount failed", fp->fs_spec); 567 continue; 568 } 569 } 570 } 571 572 if (spec != fp->fs_spec) 573 free((char *)spec); 574 } 575 if (gotone == 0) 576 exit(1); 577 } 578 579 static void 580 usage() 581 { 582 const char *progname = getprogname(); 583 584 fprintf(stderr, "usage: %s -A [-p priority] [-t blk|noblk]\n", 585 progname); 586 fprintf(stderr, " %s -D dumppath\n", progname); 587 fprintf(stderr, " %s -U [-t blk|noblk]\n", progname); 588 fprintf(stderr, " %s -a [-p priority] path\n", progname); 589 fprintf(stderr, " %s -c -p priority path\n", progname); 590 fprintf(stderr, " %s -d path\n", progname); 591 fprintf(stderr, " %s -l | -s [-k|-m|-g|-h]\n", progname); 592 fprintf(stderr, " %s -z\n", progname); 593 exit(1); 594 } 595