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