1 /* 2 * Copyright (c) 1980, 1993 3 * The Regents of the University of California. All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. Neither the name of the University nor the names of its contributors 14 * may be used to endorse or promote products derived from this software 15 * without specific prior written permission. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 * 29 * @(#) Copyright (c) 1980, 1993 The Regents of the University of California. All rights reserved. 30 * @(#)swapon.c 8.1 (Berkeley) 6/5/93 31 * $FreeBSD: src/sbin/swapon/swapon.c,v 1.8.2.2 2001/07/30 10:30:11 dd Exp $ 32 */ 33 34 #include <sys/param.h> 35 #include <sys/stat.h> 36 #include <sys/sysctl.h> 37 #include <sys/linker.h> 38 #include <sys/diskslice.h> 39 #include <sys/ioctl_compat.h> 40 #include <vm/vm_param.h> 41 42 #include <err.h> 43 #include <errno.h> 44 #include <fstab.h> 45 #include <stdio.h> 46 #include <stdlib.h> 47 #include <string.h> 48 #include <unistd.h> 49 #include <fcntl.h> 50 #include <libutil.h> 51 52 static void usage(void); 53 static int swap_on_off(char *name, int doingall, int trim, int ask); 54 static char *docrypt(char *fs_spec, int pass); 55 static void swaplist(int lflag, int sflag, int hflag); 56 57 static int qflag; 58 59 enum { SWAPON, SWAPOFF, SWAPCTL } orig_prog, which_prog = SWAPCTL; 60 61 int 62 main(int argc, char **argv) 63 { 64 struct fstab *fsp; 65 char *ptr; 66 int ret; 67 int ch; 68 int doall, sflag, lflag, hflag, eflag, cflag, iflag; 69 70 if ((ptr = strrchr(argv[0], '/')) == NULL) 71 ptr = argv[0]; 72 if (strstr(ptr, "swapon")) 73 which_prog = SWAPON; 74 else if (strstr(ptr, "swapoff")) 75 which_prog = SWAPOFF; 76 orig_prog = which_prog; 77 78 sflag = lflag = hflag = doall = eflag = cflag = iflag = 0; 79 while ((ch = getopt(argc, argv, "AacdeghiklmqsU")) != -1) { 80 switch((char)ch) { 81 case 'A': 82 if (which_prog == SWAPCTL) { 83 doall = 1; 84 which_prog = SWAPON; 85 } else { 86 usage(); 87 } 88 break; 89 case 'a': 90 if (which_prog == SWAPON || which_prog == SWAPOFF) 91 doall = 1; 92 else 93 which_prog = SWAPON; 94 break; 95 case 'd': 96 if (which_prog == SWAPCTL) 97 which_prog = SWAPOFF; 98 else 99 usage(); 100 break; 101 case 'c': 102 cflag = 1; 103 break; 104 case 'e': 105 eflag = 1; 106 break; 107 case 'g': 108 hflag = 'G'; 109 break; 110 case 'h': 111 hflag = 'H'; 112 break; 113 case 'i': 114 iflag = 1; 115 break; 116 case 'k': 117 hflag = 'K'; 118 break; 119 case 'l': 120 lflag = 1; 121 break; 122 case 'm': 123 hflag = 'M'; 124 break; 125 case 'q': 126 if (which_prog == SWAPON || which_prog == SWAPOFF) 127 qflag = 1; 128 break; 129 case 's': 130 sflag = 1; 131 break; 132 case 'U': 133 if (which_prog == SWAPCTL) { 134 doall = 1; 135 which_prog = SWAPOFF; 136 } else { 137 usage(); 138 } 139 break; 140 case '?': 141 default: 142 usage(); 143 } 144 } 145 argv += optind; 146 147 ret = 0; 148 if (which_prog == SWAPON || which_prog == SWAPOFF) { 149 if (doall) { 150 while ((fsp = getfsent()) != NULL) { 151 char *fs_spec; 152 int dotrim = eflag; 153 154 if (strcmp(fsp->fs_type, FSTAB_SW)) 155 continue; 156 if (strstr(fsp->fs_mntops, "noauto")) 157 continue; 158 159 if (strstr(fsp->fs_mntops, "notrim")) 160 dotrim = 0; 161 else if (strstr(fsp->fs_mntops, "trim")) 162 dotrim = 1; 163 164 if (cflag || strstr(fsp->fs_mntops, "crypt")) 165 fs_spec = docrypt(fsp->fs_spec, 1); 166 else 167 fs_spec = strdup(fsp->fs_spec); 168 if (swap_on_off(fs_spec, 1, dotrim, iflag)) { 169 ret = 1; 170 } else { 171 if (cflag || 172 strstr(fsp->fs_mntops, "crypt")) { 173 docrypt(fsp->fs_spec, 2); 174 } 175 if (!qflag) { 176 printf("%s: %sing %s as swap " 177 "device\n", 178 getprogname(), 179 (which_prog == SWAPOFF ? 180 "remov" : "add"), 181 fs_spec); 182 } 183 } 184 free(fs_spec); 185 } 186 } else if (*argv == NULL) { 187 usage(); 188 } 189 for (; *argv; ++argv) { 190 char *ospec = getdevpath(*argv, 0); 191 char *fs_spec; 192 if (cflag) 193 fs_spec = docrypt(ospec, 1); 194 else 195 fs_spec = strdup(ospec); 196 if (swap_on_off(fs_spec, 0, eflag, iflag)) { 197 ret = 1; 198 } else { 199 if (cflag) 200 docrypt(ospec, 2); 201 if (!qflag) { 202 printf("%s: %sing %s as swap device\n", 203 getprogname(), 204 (which_prog == SWAPOFF ? 205 "remov" : "add"), 206 fs_spec); 207 } 208 } 209 free(fs_spec); 210 } 211 } else { 212 if (lflag || sflag) 213 swaplist(lflag, sflag, hflag); 214 else 215 usage(); 216 } 217 exit(ret); 218 } 219 220 static 221 char * 222 docrypt(char *fs_spec, int pass) 223 { 224 char *id; 225 char *res; 226 char *buf; 227 228 if ((id = strrchr(fs_spec, '/')) == NULL) 229 id = fs_spec; 230 else 231 ++id; 232 asprintf(&id, "swap-%s", id); 233 asprintf(&res, "/dev/mapper/%s", id); 234 235 switch(which_prog) { 236 case SWAPOFF: 237 if (pass != 2) 238 break; 239 asprintf(&buf, "/sbin/cryptsetup remove %s", id); 240 system(buf); 241 free(buf); 242 free(id); 243 break; 244 case SWAPON: 245 if (pass != 1) 246 break; 247 if (kldfind("dm_target_crypt") < 0) 248 kldload("dm_target_crypt"); 249 250 asprintf(&buf, 251 "/sbin/cryptsetup --key-file /dev/urandom " 252 "--key-size 256 create %s %s", 253 id, fs_spec); 254 if (qflag == 0) 255 printf("%s\n", buf); 256 system(buf); 257 free(buf); 258 free(id); 259 260 /* 261 * NOTE: Don't revert to /dev/da* on error because this could 262 * inadvertently add both /dev/da* and 263 * /dev/mapper/swap-da*. 264 * 265 * Allow the swapon operation to report failure or 266 * report a duplicate. 267 */ 268 break; 269 default: 270 free(res); 271 free(id); 272 res = strdup(fs_spec); 273 break; 274 } 275 276 if (pass == 2) { 277 free (res); 278 res = NULL; 279 } 280 return res; 281 } 282 283 /* 284 * TRIM the device 285 */ 286 static 287 void 288 trim_volume(char * name) 289 { 290 struct partinfo pinfo; 291 int fd,i,n; 292 size_t bytes = 0,ksize; 293 char *xswbuf; 294 struct xswdev *xsw; 295 296 297 /* 298 * Determine if this device is already being used by swap without 299 * calling swapon(). 300 */ 301 if ((sysctlbyname("vm.swap_info_array", NULL, &bytes, NULL, 0) < 0) || 302 bytes == 0) { 303 err(1, "sysctlbyname()"); 304 } 305 306 xswbuf = malloc(bytes); 307 if ((sysctlbyname("vm.swap_info_array", xswbuf, &bytes, NULL, 0) < 0) || 308 bytes == 0) { 309 free(xswbuf); 310 err(1, "sysctlbyname()"); 311 } 312 313 ksize = ((struct xswdev *)xswbuf)->xsw_size; 314 n = (int)(bytes / ksize); 315 for (i = 0; i < n; ++i) { 316 xsw = (void *)((char *)xswbuf + i * ksize); 317 318 if (xsw->xsw_dev == NODEV ) 319 continue; 320 if(!strcmp(devname(xsw->xsw_dev, S_IFCHR), 321 name + strlen("/dev/"))) { 322 warnx("%s: device already a swap device", name); 323 exit(1); 324 } 325 } 326 327 /* 328 * Get the size and offset of this parititon/device 329 */ 330 fd = open(name, O_RDWR); 331 if (fd < 0) 332 err(1, "Unable to open %s R+W", name); 333 if (ioctl(fd, DIOCGPART, &pinfo) < 0) { 334 printf("Cannot trim regular file\n"); 335 usage (); 336 } 337 off_t ioarg[2]; 338 339 /*Trim the Device*/ 340 ioarg[0] = pinfo.media_offset; 341 ioarg[1] = pinfo.media_size; 342 printf("Trimming Device:%s, sectors (%llu -%llu)\n",name, 343 (unsigned long long)ioarg[0]/512, 344 (unsigned long long)ioarg[1]/512); 345 if (ioctl(fd, IOCTLTRIM, ioarg) < 0) { 346 printf("Device trim failed\n"); 347 usage (); 348 } 349 close(fd); 350 } 351 352 static int 353 swap_on_off(char *name, int doingall, int trim, int ask) 354 { 355 356 if (ask && which_prog == SWAPON) { 357 printf("Do you really want to use device %s as a swap device ?\n", name); 358 printf("You might loose data. [Y/N]"); 359 360 int c = fgetc(stdin); 361 printf("\n"); 362 if (c != 'y' && c != 'Y') 363 return(1); 364 365 } 366 if (which_prog == SWAPON && trim) { 367 char sysctl_name[64]; 368 int trim_enabled = 0; 369 size_t olen = sizeof(trim_enabled); 370 char *dev_name = strdup(name); 371 dev_name = strtok(dev_name + strlen("/dev/da"),"s"); 372 sprintf(sysctl_name, "kern.cam.da.%s.trim_enabled", dev_name); 373 if (sysctlbyname(sysctl_name, &trim_enabled, &olen, NULL, 0) < 0) { 374 if (qflag == 0) { 375 printf("TRIM not supported on %s, " 376 "ignoring\n", 377 name); 378 } 379 errno = 0; 380 } else if (!trim_enabled) { 381 if (qflag == 0) { 382 printf("TRIM not enabled on %s (%s), " 383 "ignoring\n", 384 name, sysctl_name); 385 } 386 } else { 387 trim_volume(name); 388 } 389 } 390 if ((which_prog == SWAPOFF ? swapoff(name) : swapon(name)) == -1) { 391 switch(errno) { 392 case EBUSY: 393 if (!doingall) 394 warnx("%s: device already in use", name); 395 break; 396 case EINVAL: 397 if (which_prog == SWAPON) 398 warnx("%s: NSWAPDEV limit reached", name); 399 else if (!doingall) 400 warn("%s", name); 401 break; 402 default: 403 warn("%s", name); 404 break; 405 } 406 return(1); 407 } 408 return(0); 409 } 410 411 static void 412 usage(void) 413 { 414 fprintf(stderr, "usage: %s ", getprogname()); 415 switch (orig_prog) { 416 case SWAPON: 417 case SWAPOFF: 418 fprintf(stderr, "-aeiq | file ...\n"); 419 break; 420 case SWAPCTL: 421 fprintf(stderr, "[-AeghiklmsU] [-a file ... | -d file ...]\n"); 422 break; 423 } 424 exit(1); 425 } 426 427 static void 428 sizetobuf(char *buf, size_t bufsize, int hflag, long long val, int hlen, 429 long blocksize) 430 { 431 if (hflag == 'H') { 432 char tmp[16]; 433 434 humanize_number(tmp, 5, (int64_t)val, "", HN_AUTOSCALE, 435 HN_B | HN_NOSPACE | HN_DECIMAL); 436 snprintf(buf, bufsize, "%*s", hlen, tmp); 437 } else { 438 snprintf(buf, bufsize, "%*lld", hlen, val / blocksize); 439 } 440 } 441 442 static void 443 swaplist(int lflag, int sflag, int hflag) 444 { 445 size_t ksize, bytes = 0; 446 char *xswbuf; 447 struct xswdev *xsw; 448 int hlen, pagesize; 449 int i, n; 450 long blocksize; 451 long long total, used, tmp_total, tmp_used; 452 char buf[32]; 453 454 pagesize = getpagesize(); 455 switch(hflag) { 456 case 'G': 457 blocksize = 1024 * 1024 * 1024; 458 strlcpy(buf, "1GB-blocks", sizeof(buf)); 459 hlen = 10; 460 break; 461 case 'H': 462 blocksize = -1; 463 strlcpy(buf, "Bytes", sizeof(buf)); 464 hlen = 10; 465 break; 466 case 'K': 467 blocksize = 1024; 468 strlcpy(buf, "1kB-blocks", sizeof(buf)); 469 hlen = 10; 470 break; 471 case 'M': 472 blocksize = 1024 * 1024; 473 strlcpy(buf, "1MB-blocks", sizeof(buf)); 474 hlen = 10; 475 break; 476 default: 477 getbsize(&hlen, &blocksize); 478 snprintf(buf, sizeof(buf), "%ld-blocks", blocksize); 479 break; 480 } 481 482 if (sysctlbyname("vm.swap_info_array", NULL, &bytes, NULL, 0) < 0) 483 err(1, "sysctlbyname()"); 484 if (bytes == 0) 485 err(1, "sysctlbyname()"); 486 487 xswbuf = malloc(bytes); 488 if (sysctlbyname("vm.swap_info_array", xswbuf, &bytes, NULL, 0) < 0) { 489 free(xswbuf); 490 err(1, "sysctlbyname()"); 491 } 492 if (bytes == 0) { 493 free(xswbuf); 494 err(1, "sysctlbyname()"); 495 } 496 497 /* 498 * Calculate size of xsw entry returned by kernel (it can be larger 499 * than the one we have if there is a version mismatch). 500 */ 501 ksize = ((struct xswdev *)xswbuf)->xsw_size; 502 n = (int)(bytes / ksize); 503 504 if (lflag) { 505 printf("%-13s %*s %*s\n", 506 "Device:", 507 hlen, buf, 508 hlen, "Used:"); 509 } 510 511 total = used = tmp_total = tmp_used = 0; 512 for (i = 0; i < n; ++i) { 513 xsw = (void *)((char *)xswbuf + i * ksize); 514 515 if (xsw->xsw_nblks == 0) 516 continue; 517 518 tmp_total = (long long)xsw->xsw_nblks * pagesize; 519 tmp_used = (long long)xsw->xsw_used * pagesize; 520 total += tmp_total; 521 used += tmp_used; 522 if (lflag) { 523 sizetobuf(buf, sizeof(buf), hflag, tmp_total, hlen, 524 blocksize); 525 if (xsw->xsw_dev == NODEV) { 526 printf("%-13s %s ", "[NFS swap]", buf); 527 } else { 528 printf("/dev/%-8s %s ", 529 devname(xsw->xsw_dev, S_IFCHR), buf); 530 } 531 532 sizetobuf(buf, sizeof(buf), hflag, tmp_used, hlen, 533 blocksize); 534 printf("%s\n", buf); 535 } 536 } 537 538 if (sflag) { 539 sizetobuf(buf, sizeof(buf), hflag, total, hlen, blocksize); 540 printf("Total: %s ", buf); 541 sizetobuf(buf, sizeof(buf), hflag, used, hlen, blocksize); 542 printf("%s\n", buf); 543 } 544 } 545