xref: /dragonfly/sbin/swapon/swapon.c (revision f2a91d31)
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(id);
242 		break;
243 	case SWAPON:
244 		if (pass != 1)
245 			break;
246 		if (kldfind("dm_target_crypt") < 0)
247 			kldload("dm_target_crypt");
248 
249 		asprintf(&buf,
250 			 "/sbin/cryptsetup --key-file /dev/urandom "
251 			 "--key-size 256 create %s %s",
252 			 id, fs_spec);
253 		if (qflag == 0)
254 			printf("%s\n", buf);
255 		system(buf);
256 		free(buf);
257 		free(id);
258 
259 		/*
260 		 * NOTE: Don't revert to /dev/da* on error because this could
261 		 *	 inadvertently add both /dev/da* and
262 		 *	 /dev/mapper/swap-da*.
263 		 *
264 		 *	 Allow the swapon operation to report failure or
265 		 *	 report a duplicate.
266 		 */
267 		break;
268 	default:
269 		free(res);
270 		free(id);
271 		res = strdup(fs_spec);
272 		break;
273 	}
274 
275 	if (pass == 2) {
276 		free (res);
277 		res = NULL;
278 	}
279 	return res;
280 }
281 
282 /*
283  * TRIM the device
284  */
285 static
286 void
287 trim_volume(char * name)
288 {
289 	struct partinfo pinfo;
290 	int fd,i,n;
291 	size_t bytes = 0,ksize;
292 	char *xswbuf;
293 	struct xswdev *xsw;
294 
295 
296 	/*
297 	* Determine if this device is already being used by swap without
298 	* calling swapon().
299 	*/
300 	if ((sysctlbyname("vm.swap_info_array", NULL, &bytes, NULL, 0) < 0) ||
301 	    bytes == 0) {
302 		err(1, "sysctlbyname()");
303 	}
304 
305 	xswbuf = malloc(bytes);
306 	if ((sysctlbyname("vm.swap_info_array", xswbuf, &bytes, NULL, 0) < 0) ||
307 	    bytes == 0) {
308 			free(xswbuf);
309 			err(1, "sysctlbyname()");
310 	}
311 
312 	ksize = ((struct xswdev *)xswbuf)->xsw_size;
313 	n = (int)(bytes / ksize);
314 	for (i = 0; i < n; ++i) {
315 		xsw = (void *)((char *)xswbuf + i * ksize);
316 
317 		if (xsw->xsw_dev == NODEV )
318 			continue;
319 		if(!strcmp(devname(xsw->xsw_dev, S_IFCHR),
320 		    name + strlen("/dev/"))) {
321 			warnx("%s: device already a swap device", name);
322 			exit(1);
323 		}
324 	}
325 
326 	/*
327 	 * Get the size and offset of this parititon/device
328 	 */
329 	fd = open(name, O_RDWR);
330 	if (fd < 0)
331 		err(1, "Unable to open %s R+W", name);
332 	if (ioctl(fd, DIOCGPART, &pinfo) < 0) {
333 		printf("Cannot trim regular file\n");
334 		usage ();
335 	}
336 	off_t ioarg[2];
337 
338 	/*Trim the Device*/
339 	ioarg[0] = pinfo.media_offset;
340 	ioarg[1] = pinfo.media_size;
341 	printf("Trimming Device:%s, sectors (%llu -%llu)\n",name,
342 	     (unsigned long long)ioarg[0]/512,
343 	     (unsigned long long)ioarg[1]/512);
344 	if (ioctl(fd, IOCTLTRIM, ioarg) < 0) {
345 		printf("Device trim failed\n");
346 		usage ();
347 	}
348 	close(fd);
349 }
350 
351 static int
352 swap_on_off(char *name, int doingall, int trim, int ask)
353 {
354 
355 	if (ask && which_prog == SWAPON) {
356 		printf("Do you really want to use device %s as a swap device ?\n", name);
357 		printf("You might loose data. [Y/N]");
358 
359 		int c = fgetc(stdin);
360 		printf("\n");
361 		if (c != 'y' && c != 'Y')
362 			return(1);
363 
364 	}
365 	if (which_prog == SWAPON && trim) {
366 		char sysctl_name[64];
367 		int trim_enabled = 0;
368 		size_t olen = sizeof(trim_enabled);
369 		char *dev_name = strdup(name);
370 		dev_name = strtok(dev_name + strlen("/dev/da"),"s");
371 		sprintf(sysctl_name, "kern.cam.da.%s.trim_enabled", dev_name);
372 		if (sysctlbyname(sysctl_name, &trim_enabled, &olen, NULL, 0) < 0) {
373 			if (qflag == 0) {
374 				printf("TRIM not supported on %s, "
375 				       "ignoring\n",
376 				       name);
377 			}
378 			errno = 0;
379 		} else if (!trim_enabled) {
380 			if (qflag == 0) {
381 				printf("TRIM not enabled on %s (%s), "
382 				       "ignoring\n",
383 				       name, sysctl_name);
384 			}
385 		} else {
386 			trim_volume(name);
387 		}
388 	}
389 	if ((which_prog == SWAPOFF ? swapoff(name) : swapon(name)) == -1) {
390 		switch(errno) {
391 		case EBUSY:
392 			if (!doingall)
393 				warnx("%s: device already in use", name);
394 			break;
395 		case EINVAL:
396 			if (which_prog == SWAPON)
397 				warnx("%s: NSWAPDEV limit reached", name);
398 			else if (!doingall)
399 				warn("%s", name);
400 			break;
401 		default:
402 			warn("%s", name);
403 			break;
404 		}
405 		return(1);
406 	}
407 	return(0);
408 }
409 
410 static void
411 usage(void)
412 {
413 	fprintf(stderr, "usage: %s ", getprogname());
414 	switch (orig_prog) {
415 	case SWAPON:
416 	case SWAPOFF:
417 		fprintf(stderr, "-aeiq | file ...\n");
418 		break;
419 	case SWAPCTL:
420 		fprintf(stderr, "[-AeghiklmsU] [-a file ... | -d file ...]\n");
421 		break;
422 	}
423 	exit(1);
424 }
425 
426 static void
427 sizetobuf(char *buf, size_t bufsize, int hflag, long long val, int hlen,
428     long blocksize)
429 {
430 	if (hflag == 'H') {
431 		char tmp[16];
432 
433 		humanize_number(tmp, 5, (int64_t)val, "", HN_AUTOSCALE,
434 		    HN_B | HN_NOSPACE | HN_DECIMAL);
435 		snprintf(buf, bufsize, "%*s", hlen, tmp);
436 	} else {
437 		snprintf(buf, bufsize, "%*lld", hlen, val / blocksize);
438 	}
439 }
440 
441 static void
442 swaplist(int lflag, int sflag, int hflag)
443 {
444 	size_t ksize, bytes = 0;
445 	char *xswbuf;
446 	struct xswdev *xsw;
447 	int hlen, pagesize;
448 	int i, n;
449 	long blocksize;
450 	long long total, used, tmp_total, tmp_used;
451 	char buf[32];
452 
453 	pagesize = getpagesize();
454 	switch(hflag) {
455 	case 'G':
456 		blocksize = 1024 * 1024 * 1024;
457 		strlcpy(buf, "1GB-blocks", sizeof(buf));
458 		hlen = 10;
459 		break;
460 	case 'H':
461 		blocksize = -1;
462 		strlcpy(buf, "Bytes", sizeof(buf));
463 		hlen = 10;
464 		break;
465 	case 'K':
466 		blocksize = 1024;
467 		strlcpy(buf, "1kB-blocks", sizeof(buf));
468 		hlen = 10;
469 		break;
470 	case 'M':
471 		blocksize = 1024 * 1024;
472 		strlcpy(buf, "1MB-blocks", sizeof(buf));
473 		hlen = 10;
474 		break;
475 	default:
476 		getbsize(&hlen, &blocksize);
477 		snprintf(buf, sizeof(buf), "%ld-blocks", blocksize);
478 		break;
479 	}
480 
481 	if (sysctlbyname("vm.swap_info_array", NULL, &bytes, NULL, 0) < 0)
482 		err(1, "sysctlbyname()");
483 	if (bytes == 0)
484 		err(1, "sysctlbyname()");
485 
486 	xswbuf = malloc(bytes);
487 	if (sysctlbyname("vm.swap_info_array", xswbuf, &bytes, NULL, 0) < 0) {
488 		free(xswbuf);
489 		err(1, "sysctlbyname()");
490 	}
491 	if (bytes == 0) {
492 		free(xswbuf);
493 		err(1, "sysctlbyname()");
494 	}
495 
496 	/*
497 	 * Calculate size of xsw entry returned by kernel (it can be larger
498 	 * than the one we have if there is a version mismatch).
499 	 */
500 	ksize = ((struct xswdev *)xswbuf)->xsw_size;
501 	n = (int)(bytes / ksize);
502 
503 	if (lflag) {
504 		printf("%-13s %*s %*s\n",
505 		    "Device:",
506 		    hlen, buf,
507 		    hlen, "Used:");
508 	}
509 
510 	total = used = tmp_total = tmp_used = 0;
511 	for (i = 0; i < n; ++i) {
512 		xsw = (void *)((char *)xswbuf + i * ksize);
513 
514 		if (xsw->xsw_nblks == 0)
515 			continue;
516 
517 		tmp_total = (long long)xsw->xsw_nblks * pagesize;
518 		tmp_used = (long long)xsw->xsw_used * pagesize;
519 		total += tmp_total;
520 		used += tmp_used;
521 		if (lflag) {
522 			sizetobuf(buf, sizeof(buf), hflag, tmp_total, hlen,
523 			    blocksize);
524 			if (xsw->xsw_dev == NODEV) {
525 				printf("%-13s %s ", "[NFS swap]", buf);
526 			} else {
527 				printf("/dev/%-8s %s ",
528 				    devname(xsw->xsw_dev, S_IFCHR), buf);
529 			}
530 
531 			sizetobuf(buf, sizeof(buf), hflag, tmp_used, hlen,
532 			    blocksize);
533 			printf("%s\n", buf);
534 		}
535 	}
536 
537 	if (sflag) {
538 		sizetobuf(buf, sizeof(buf), hflag, total, hlen, blocksize);
539 		printf("Total:        %s ", buf);
540 		sizetobuf(buf, sizeof(buf), hflag, used, hlen, blocksize);
541 		printf("%s\n", buf);
542 	}
543 }
544