xref: /dragonfly/sbin/swapon/swapon.c (revision 4bab7bf3)
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