xref: /dragonfly/sbin/swapon/swapon.c (revision 3d33658b)
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 <vm/vm_param.h>
40 
41 #include <err.h>
42 #include <errno.h>
43 #include <fstab.h>
44 #include <stdio.h>
45 #include <stdlib.h>
46 #include <string.h>
47 #include <unistd.h>
48 #include <fcntl.h>
49 #include <libutil.h>
50 
51 #include <bus/cam/scsi/scsi_daio.h>
52 
53 static void usage(void);
54 static int swap_on_off(char *name, int doingall, int trim, int ask);
55 static char *docrypt(char *fs_spec, int pass);
56 static void swaplist(int lflag, int sflag, int hflag);
57 
58 static int qflag;
59 
60 enum { SWAPON, SWAPOFF, SWAPCTL } orig_prog, which_prog = SWAPCTL;
61 
62 int
63 main(int argc, char **argv)
64 {
65 	struct fstab *fsp;
66 	char *ptr;
67 	int ret;
68 	int ch;
69 	int doall, sflag, lflag, hflag, eflag, cflag, iflag;
70 
71 	if ((ptr = strrchr(argv[0], '/')) == NULL)
72 		ptr = argv[0];
73 	if (strstr(ptr, "swapon"))
74 		which_prog = SWAPON;
75 	else if (strstr(ptr, "swapoff"))
76 		which_prog = SWAPOFF;
77 	orig_prog = which_prog;
78 
79 	sflag = lflag = hflag = doall = eflag = cflag = iflag = 0;
80 	while ((ch = getopt(argc, argv, "acdeghiklmqsAEU")) != -1) {
81 		switch((char)ch) {
82 		case 'A':
83 			if (which_prog == SWAPCTL) {
84 				doall = 1;
85 				which_prog = SWAPON;
86 			} else {
87 				usage();
88 			}
89 			break;
90 		case 'a':
91 			if (which_prog == SWAPON || which_prog == SWAPOFF)
92 				doall = 1;
93 			else
94 				which_prog = SWAPON;
95 			break;
96 		case 'd':
97 			if (which_prog == SWAPCTL)
98 				which_prog = SWAPOFF;
99 			else
100 				usage();
101 			break;
102 		case 'c':
103 			cflag = 1;
104 			break;
105 		case 'E':
106 		case 'e':
107 			eflag = 1;
108 			break;
109 		case 'g':
110 			hflag = 'G';
111 			break;
112 		case 'h':
113 			hflag = 'H';
114 			break;
115 		case 'i':
116 			iflag = 1;
117 			break;
118 		case 'k':
119 			hflag = 'K';
120 			break;
121 		case 'l':
122 			lflag = 1;
123 			break;
124 		case 'm':
125 			hflag = 'M';
126 			break;
127 		case 'q':
128 			if (which_prog == SWAPON || which_prog == SWAPOFF)
129 				qflag = 1;
130 			break;
131 		case 's':
132 			sflag = 1;
133 			break;
134 		case 'U':
135 			if (which_prog == SWAPCTL) {
136 				doall = 1;
137 				which_prog = SWAPOFF;
138 			} else {
139 				usage();
140 			}
141 			break;
142 		case '?':
143 		default:
144 			usage();
145 		}
146 	}
147 	argv += optind;
148 
149 	ret = 0;
150 	if (which_prog == SWAPON || which_prog == SWAPOFF) {
151 		if (doall) {
152 			while ((fsp = getfsent()) != NULL) {
153 				char *fs_spec;
154 				int dotrim = eflag;
155 
156 				if (strcmp(fsp->fs_type, FSTAB_SW))
157 					continue;
158 				if (strstr(fsp->fs_mntops, "noauto"))
159 					continue;
160 
161 				if (strstr(fsp->fs_mntops, "notrim"))
162 					dotrim = 0;
163 				else if (strstr(fsp->fs_mntops, "trim"))
164 					dotrim = 1;
165 
166 				if (cflag || strstr(fsp->fs_mntops, "crypt"))
167 					fs_spec = docrypt(fsp->fs_spec, 1);
168 				else
169 					fs_spec = strdup(fsp->fs_spec);
170 				if (swap_on_off(fs_spec, 1, dotrim, iflag)) {
171 					ret = 1;
172 				} else {
173 					if (cflag ||
174 					    strstr(fsp->fs_mntops, "crypt")) {
175 						docrypt(fsp->fs_spec, 2);
176 					}
177 					if (!qflag) {
178 						printf("%s: %sing %s as swap "
179 						       "device\n",
180 						    getprogname(),
181 						    (which_prog == SWAPOFF ?
182 							    "remov" : "add"),
183 						    fs_spec);
184 					}
185 				}
186 				free(fs_spec);
187 			}
188 		} else if (*argv == NULL) {
189 			usage();
190 		}
191 		for (; *argv; ++argv) {
192 			char *ospec = getdevpath(*argv, 0);
193 			char *fs_spec;
194 			if (cflag)
195 				fs_spec = docrypt(ospec, 1);
196 			else
197 				fs_spec = strdup(ospec);
198 			if (swap_on_off(fs_spec, 0, eflag, iflag)) {
199 				ret = 1;
200 			} else {
201 				if (cflag)
202 					docrypt(ospec, 2);
203 				if (!qflag) {
204 					printf("%s: %sing %s as swap device\n",
205 					    getprogname(),
206 					    (which_prog == SWAPOFF ?
207 						"remov" : "add"),
208 					    fs_spec);
209 				}
210 			}
211 			free(fs_spec);
212 		}
213 	} else {
214 		if (lflag || sflag)
215 			swaplist(lflag, sflag, hflag);
216 		else
217 			usage();
218 	}
219 	exit(ret);
220 }
221 
222 static
223 char *
224 docrypt(char *fs_spec, int pass)
225 {
226 	char *id;
227 	char *res;
228 	char *buf;
229 
230 	if ((id = strrchr(fs_spec, '/')) == NULL)
231 		id = fs_spec;
232 	else
233 		++id;
234 	asprintf(&id, "swap-%s", id);
235 	asprintf(&res, "/dev/mapper/%s", id);
236 
237 	switch(which_prog) {
238 	case SWAPOFF:
239 		if (pass != 2)
240 			break;
241 		asprintf(&buf, "/sbin/cryptsetup remove %s", id);
242 		system(buf);
243 		free(buf);
244 		free(id);
245 		break;
246 	case SWAPON:
247 		if (pass != 1)
248 			break;
249 		if (kldfind("dm_target_crypt") < 0)
250 			kldload("dm_target_crypt");
251 
252 		asprintf(&buf,
253 			 "/sbin/cryptsetup --key-file /dev/urandom "
254 			 "--key-size 256 create %s %s",
255 			 id, fs_spec);
256 		if (qflag == 0)
257 			printf("%s\n", buf);
258 		system(buf);
259 		free(buf);
260 		free(id);
261 
262 		/*
263 		 * NOTE: Don't revert to /dev/da* on error because this could
264 		 *	 inadvertently add both /dev/da* and
265 		 *	 /dev/mapper/swap-da*.
266 		 *
267 		 *	 Allow the swapon operation to report failure or
268 		 *	 report a duplicate.
269 		 */
270 		break;
271 	default:
272 		free(res);
273 		free(id);
274 		res = strdup(fs_spec);
275 		break;
276 	}
277 
278 	if (pass == 2) {
279 		free (res);
280 		res = NULL;
281 	}
282 	return res;
283 }
284 
285 /*
286  * TRIM the device
287  */
288 static
289 void
290 trim_volume(char * name)
291 {
292 	struct partinfo pinfo;
293 	int fd,i,n;
294 	size_t bytes = 0,ksize;
295 	char *xswbuf;
296 	struct xswdev *xsw;
297 
298 
299 	/*
300 	* Determine if this device is already being used by swap without
301 	* calling swapon().
302 	*/
303 	if ((sysctlbyname("vm.swap_info_array", NULL, &bytes, NULL, 0) < 0) ||
304 	    bytes == 0) {
305 		err(1, "sysctlbyname()");
306 	}
307 
308 	xswbuf = malloc(bytes);
309 	if ((sysctlbyname("vm.swap_info_array", xswbuf, &bytes, NULL, 0) < 0) ||
310 	    bytes == 0) {
311 			free(xswbuf);
312 			err(1, "sysctlbyname()");
313 	}
314 
315 	ksize = ((struct xswdev *)xswbuf)->xsw_size;
316 	n = (int)(bytes / ksize);
317 	for (i = 0; i < n; ++i) {
318 		xsw = (void *)((char *)xswbuf + i * ksize);
319 
320 		if (xsw->xsw_dev == NODEV )
321 			continue;
322 		if(!strcmp(devname(xsw->xsw_dev, S_IFCHR),
323 		    name + strlen("/dev/"))) {
324 			warnx("%s: device already a swap device", name);
325 			exit(1);
326 		}
327 	}
328 
329 	/*
330 	 * Get the size and offset of this parititon/device
331 	 */
332 	fd = open(name, O_RDWR);
333 	if (fd < 0)
334 		err(1, "Unable to open %s R+W", name);
335 	if (ioctl(fd, DIOCGPART, &pinfo) < 0) {
336 		printf("Cannot trim regular file\n");
337 		usage ();
338 	}
339 	off_t ioarg[2];
340 
341 	/*Trim the Device*/
342 	ioarg[0] = pinfo.media_offset;
343 	ioarg[1] = pinfo.media_size;
344 	printf("Trimming Device:%s, start=%jd bytes=%jd)\n",
345 	     name, (intmax_t)ioarg[0], (intmax_t)ioarg[1]);
346 	if (ioctl(fd, DAIOCTRIM, ioarg) < 0) {
347 		printf("Device trim failed\n");
348 		usage ();
349 	}
350 	close(fd);
351 }
352 
353 static int
354 swap_on_off(char *name, int doingall, int trim, int ask)
355 {
356 
357 	if (ask && which_prog == SWAPON) {
358 		printf("Do you really want to use device %s as a swap device ?\n", name);
359 		printf("You might loose data. [Y/N]");
360 
361 		int c = fgetc(stdin);
362 		printf("\n");
363 		if (c != 'y' && c != 'Y')
364 			return(1);
365 
366 	}
367 	if (which_prog == SWAPON && trim) {
368 		trim_volume(name);
369 	}
370 	if ((which_prog == SWAPOFF ? swapoff(name) : swapon(name)) == -1) {
371 		switch(errno) {
372 		case EBUSY:
373 			if (!doingall)
374 				warnx("%s: device already in use", name);
375 			break;
376 		case EINVAL:
377 			if (which_prog == SWAPON)
378 				warnx("%s: NSWAPDEV limit reached", name);
379 			else if (!doingall)
380 				warn("%s", name);
381 			break;
382 		default:
383 			warn("%s", name);
384 			break;
385 		}
386 		return(1);
387 	}
388 	return(0);
389 }
390 
391 static void
392 usage(void)
393 {
394 	fprintf(stderr, "usage: %s ", getprogname());
395 	switch (orig_prog) {
396 	case SWAPON:
397 	case SWAPOFF:
398 		fprintf(stderr, "-aeiq | file ...\n");
399 		break;
400 	case SWAPCTL:
401 		fprintf(stderr, "[-AeghiklmsU] [-a file ... | -d file ...]\n");
402 		break;
403 	}
404 	exit(1);
405 }
406 
407 static void
408 sizetobuf(char *buf, size_t bufsize, int hflag, long long val, int hlen,
409     long blocksize)
410 {
411 	if (hflag == 'H') {
412 		char tmp[16];
413 
414 		humanize_number(tmp, 5, (int64_t)val, "", HN_AUTOSCALE,
415 		    HN_B | HN_NOSPACE | HN_DECIMAL);
416 		snprintf(buf, bufsize, "%*s", hlen, tmp);
417 	} else {
418 		snprintf(buf, bufsize, "%*lld", hlen, val / blocksize);
419 	}
420 }
421 
422 static void
423 swaplist(int lflag, int sflag, int hflag)
424 {
425 	size_t ksize, bytes = 0;
426 	char *xswbuf;
427 	struct xswdev *xsw;
428 	int hlen, pagesize;
429 	int i, n;
430 	long blocksize;
431 	long long total, used, tmp_total, tmp_used;
432 	char buf[32];
433 
434 	pagesize = getpagesize();
435 	switch(hflag) {
436 	case 'G':
437 		blocksize = 1024 * 1024 * 1024;
438 		strlcpy(buf, "1GB-blocks", sizeof(buf));
439 		hlen = 10;
440 		break;
441 	case 'H':
442 		blocksize = -1;
443 		strlcpy(buf, "Bytes", sizeof(buf));
444 		hlen = 10;
445 		break;
446 	case 'K':
447 		blocksize = 1024;
448 		strlcpy(buf, "1kB-blocks", sizeof(buf));
449 		hlen = 10;
450 		break;
451 	case 'M':
452 		blocksize = 1024 * 1024;
453 		strlcpy(buf, "1MB-blocks", sizeof(buf));
454 		hlen = 10;
455 		break;
456 	default:
457 		getbsize(&hlen, &blocksize);
458 		snprintf(buf, sizeof(buf), "%ld-blocks", blocksize);
459 		break;
460 	}
461 
462 	if (sysctlbyname("vm.swap_info_array", NULL, &bytes, NULL, 0) < 0)
463 		err(1, "sysctlbyname()");
464 	if (bytes == 0)
465 		err(1, "sysctlbyname()");
466 
467 	xswbuf = malloc(bytes);
468 	if (sysctlbyname("vm.swap_info_array", xswbuf, &bytes, NULL, 0) < 0) {
469 		free(xswbuf);
470 		err(1, "sysctlbyname()");
471 	}
472 	if (bytes == 0) {
473 		free(xswbuf);
474 		err(1, "sysctlbyname()");
475 	}
476 
477 	/*
478 	 * Calculate size of xsw entry returned by kernel (it can be larger
479 	 * than the one we have if there is a version mismatch).
480 	 */
481 	ksize = ((struct xswdev *)xswbuf)->xsw_size;
482 	n = (int)(bytes / ksize);
483 
484 	if (lflag) {
485 		printf("%-13s %*s %*s\n",
486 		    "Device:",
487 		    hlen, buf,
488 		    hlen, "Used:");
489 	}
490 
491 	total = used = tmp_total = tmp_used = 0;
492 	for (i = 0; i < n; ++i) {
493 		xsw = (void *)((char *)xswbuf + i * ksize);
494 
495 		if (xsw->xsw_nblks == 0)
496 			continue;
497 
498 		tmp_total = (long long)xsw->xsw_nblks * pagesize;
499 		tmp_used = (long long)xsw->xsw_used * pagesize;
500 		total += tmp_total;
501 		used += tmp_used;
502 		if (lflag) {
503 			sizetobuf(buf, sizeof(buf), hflag, tmp_total, hlen,
504 			    blocksize);
505 			if (xsw->xsw_dev == NODEV) {
506 				printf("%-13s %s ", "[NFS swap]", buf);
507 			} else {
508 				printf("/dev/%-8s %s ",
509 				    devname(xsw->xsw_dev, S_IFCHR), buf);
510 			}
511 
512 			sizetobuf(buf, sizeof(buf), hflag, tmp_used, hlen,
513 			    blocksize);
514 			printf("%s\n", buf);
515 		}
516 	}
517 
518 	if (sflag) {
519 		sizetobuf(buf, sizeof(buf), hflag, total, hlen, blocksize);
520 		printf("Total:        %s ", buf);
521 		sizetobuf(buf, sizeof(buf), hflag, used, hlen, blocksize);
522 		printf("%s\n", buf);
523 	}
524 }
525