swapon - Fix minor memory leak
[dragonfly.git] / sbin / swapon / swapon.c
CommitLineData
984263bc
MD
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.
dc71b7ab 13 * 3. Neither the name of the University nor the names of its contributors
984263bc
MD
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.
1de703da
MD
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 $
984263bc
MD
32 */
33
9f3543c6
MD
34#include <sys/param.h>
35#include <sys/stat.h>
36#include <sys/sysctl.h>
58b7380e 37#include <sys/linker.h>
e0fb398b
T
38#include <sys/diskslice.h>
39#include <sys/ioctl_compat.h>
9f3543c6
MD
40#include <vm/vm_param.h>
41
984263bc
MD
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>
9f3543c6
MD
49#include <fcntl.h>
50#include <libutil.h>
984263bc 51
9a76cd75 52static void usage(void);
62a3b3b5 53static int swap_on_off(char *name, int doingall, int trim, int ask);
58b7380e 54static char *docrypt(char *fs_spec, int pass);
9f3543c6
MD
55static void swaplist(int lflag, int sflag, int hflag);
56
58b7380e
MD
57static int qflag;
58
9f3543c6 59enum { SWAPON, SWAPOFF, SWAPCTL } orig_prog, which_prog = SWAPCTL;
984263bc
MD
60
61int
62main(int argc, char **argv)
63{
b942f274 64 struct fstab *fsp;
9f3543c6
MD
65 char *ptr;
66 int ret;
67 int ch;
58b7380e 68 int doall, sflag, lflag, hflag, eflag, cflag, iflag;
984263bc 69
9f3543c6
MD
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
58b7380e
MD
78 sflag = lflag = hflag = doall = eflag = cflag = iflag = 0;
79 while ((ch = getopt(argc, argv, "AacdeghiklmqsU")) != -1) {
984263bc 80 switch((char)ch) {
9f3543c6
MD
81 case 'A':
82 if (which_prog == SWAPCTL) {
83 doall = 1;
84 which_prog = SWAPON;
85 } else {
86 usage();
87 }
88 break;
984263bc 89 case 'a':
9f3543c6
MD
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;
58b7380e
MD
101 case 'c':
102 cflag = 1;
103 break;
e0fb398b
T
104 case 'e':
105 eflag = 1;
106 break;
9f3543c6
MD
107 case 'g':
108 hflag = 'G';
109 break;
110 case 'h':
111 hflag = 'H';
112 break;
62a3b3b5
JG
113 case 'i':
114 iflag = 1;
115 break;
9f3543c6
MD
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 }
984263bc
MD
139 break;
140 case '?':
141 default:
142 usage();
143 }
9f3543c6 144 }
984263bc
MD
145 argv += optind;
146
9f3543c6
MD
147 ret = 0;
148 if (which_prog == SWAPON || which_prog == SWAPOFF) {
149 if (doall) {
150 while ((fsp = getfsent()) != NULL) {
58b7380e
MD
151 char *fs_spec;
152 int dotrim = eflag;
153
9f3543c6
MD
154 if (strcmp(fsp->fs_type, FSTAB_SW))
155 continue;
156 if (strstr(fsp->fs_mntops, "noauto"))
157 continue;
58b7380e
MD
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)) {
9f3543c6
MD
169 ret = 1;
170 } else {
58b7380e
MD
171 if (cflag ||
172 strstr(fsp->fs_mntops, "crypt")) {
173 docrypt(fsp->fs_spec, 2);
174 }
9f3543c6 175 if (!qflag) {
58b7380e
MD
176 printf("%s: %sing %s as swap "
177 "device\n",
9f3543c6 178 getprogname(),
58b7380e
MD
179 (which_prog == SWAPOFF ?
180 "remov" : "add"),
181 fs_spec);
9f3543c6
MD
182 }
183 }
58b7380e 184 free(fs_spec);
9f3543c6
MD
185 }
186 } else if (*argv == NULL) {
187 usage();
984263bc 188 }
9f3543c6 189 for (; *argv; ++argv) {
58b7380e
MD
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)) {
9f3543c6 197 ret = 1;
58b7380e
MD
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 }
9f3543c6 208 }
58b7380e 209 free(fs_spec);
9f3543c6
MD
210 }
211 } else {
212 if (lflag || sflag)
213 swaplist(lflag, sflag, hflag);
214 else
215 usage();
c4d5a9b3 216 }
9f3543c6 217 exit(ret);
984263bc
MD
218}
219
58b7380e
MD
220static
221char *
222docrypt(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);
08c0d58d 241 free(buf);
58b7380e
MD
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
e0fb398b
T
283/*
284 * TRIM the device
285 */
286static
287void
288trim_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
9f3543c6 352static int
62a3b3b5 353swap_on_off(char *name, int doingall, int trim, int ask)
984263bc 354{
62a3b3b5
JG
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);
fd459e4b 361 printf("\n");
62a3b3b5
JG
362 if (c != 'y' && c != 'Y')
363 return(1);
364
365 }
58b7380e 366 if (which_prog == SWAPON && trim) {
e0fb398b
T
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);
088b2e68 373 if (sysctlbyname(sysctl_name, &trim_enabled, &olen, NULL, 0) < 0) {
58b7380e
MD
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);
e0fb398b 388 }
e0fb398b 389 }
9f3543c6
MD
390 if ((which_prog == SWAPOFF ? swapoff(name) : swapon(name)) == -1) {
391 switch(errno) {
984263bc 392 case EBUSY:
9f3543c6 393 if (!doingall)
984263bc
MD
394 warnx("%s: device already in use", name);
395 break;
9f3543c6
MD
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;
984263bc
MD
402 default:
403 warn("%s", name);
404 break;
405 }
406 return(1);
407 }
408 return(0);
409}
410
411static void
b5744197 412usage(void)
984263bc 413{
9f3543c6
MD
414 fprintf(stderr, "usage: %s ", getprogname());
415 switch (orig_prog) {
416 case SWAPON:
417 case SWAPOFF:
62a3b3b5 418 fprintf(stderr, "-aeiq | file ...\n");
9f3543c6
MD
419 break;
420 case SWAPCTL:
62a3b3b5 421 fprintf(stderr, "[-AeghiklmsU] [-a file ... | -d file ...]\n");
9f3543c6
MD
422 break;
423 }
984263bc
MD
424 exit(1);
425}
9f3543c6
MD
426
427static void
428sizetobuf(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
442static void
443swaplist(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
ebd20800
SW
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;
9f3543c6
MD
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}