xref: /dragonfly/sbin/swapon/swapon.c (revision 0ca59c34)
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 		sysctlbyname(sysctl_name, &trim_enabled, &olen, NULL, 0);
373 		if (errno == ENOENT) {
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