xref: /dragonfly/sbin/swapon/swapon.c (revision e89cf083)
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 	}
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 		trim_volume(name);
368 	}
369 	if ((which_prog == SWAPOFF ? swapoff(name) : swapon(name)) == -1) {
370 		switch(errno) {
371 		case EBUSY:
372 			if (!doingall)
373 				warnx("%s: device already in use", name);
374 			break;
375 		case EINVAL:
376 			if (which_prog == SWAPON)
377 				warnx("%s: NSWAPDEV limit reached", name);
378 			else if (!doingall)
379 				warn("%s", name);
380 			break;
381 		default:
382 			warn("%s", name);
383 			break;
384 		}
385 		return(1);
386 	}
387 	return(0);
388 }
389 
390 static void
391 usage(void)
392 {
393 	fprintf(stderr, "usage: %s ", getprogname());
394 	switch (orig_prog) {
395 	case SWAPON:
396 	case SWAPOFF:
397 		fprintf(stderr, "-aeiq | file ...\n");
398 		break;
399 	case SWAPCTL:
400 		fprintf(stderr, "[-AeghiklmsU] [-a file ... | -d file ...]\n");
401 		break;
402 	}
403 	exit(1);
404 }
405 
406 static void
407 sizetobuf(char *buf, size_t bufsize, int hflag, long long val, int hlen,
408     long blocksize)
409 {
410 	if (hflag == 'H') {
411 		char tmp[16];
412 
413 		humanize_number(tmp, 5, (int64_t)val, "", HN_AUTOSCALE,
414 		    HN_B | HN_NOSPACE | HN_DECIMAL);
415 		snprintf(buf, bufsize, "%*s", hlen, tmp);
416 	} else {
417 		snprintf(buf, bufsize, "%*lld", hlen, val / blocksize);
418 	}
419 }
420 
421 static void
422 swaplist(int lflag, int sflag, int hflag)
423 {
424 	size_t ksize, bytes = 0;
425 	char *xswbuf;
426 	struct xswdev *xsw;
427 	int hlen, pagesize;
428 	int i, n;
429 	long blocksize;
430 	long long total, used, tmp_total, tmp_used;
431 	char buf[32];
432 
433 	pagesize = getpagesize();
434 	switch(hflag) {
435 	case 'G':
436 		blocksize = 1024 * 1024 * 1024;
437 		strlcpy(buf, "1GB-blocks", sizeof(buf));
438 		hlen = 10;
439 		break;
440 	case 'H':
441 		blocksize = -1;
442 		strlcpy(buf, "Bytes", sizeof(buf));
443 		hlen = 10;
444 		break;
445 	case 'K':
446 		blocksize = 1024;
447 		strlcpy(buf, "1kB-blocks", sizeof(buf));
448 		hlen = 10;
449 		break;
450 	case 'M':
451 		blocksize = 1024 * 1024;
452 		strlcpy(buf, "1MB-blocks", sizeof(buf));
453 		hlen = 10;
454 		break;
455 	default:
456 		getbsize(&hlen, &blocksize);
457 		snprintf(buf, sizeof(buf), "%ld-blocks", blocksize);
458 		break;
459 	}
460 
461 	if (sysctlbyname("vm.swap_info_array", NULL, &bytes, NULL, 0) < 0)
462 		err(1, "sysctlbyname()");
463 	if (bytes == 0)
464 		err(1, "sysctlbyname()");
465 
466 	xswbuf = malloc(bytes);
467 	if (sysctlbyname("vm.swap_info_array", xswbuf, &bytes, NULL, 0) < 0) {
468 		free(xswbuf);
469 		err(1, "sysctlbyname()");
470 	}
471 	if (bytes == 0) {
472 		free(xswbuf);
473 		err(1, "sysctlbyname()");
474 	}
475 
476 	/*
477 	 * Calculate size of xsw entry returned by kernel (it can be larger
478 	 * than the one we have if there is a version mismatch).
479 	 */
480 	ksize = ((struct xswdev *)xswbuf)->xsw_size;
481 	n = (int)(bytes / ksize);
482 
483 	if (lflag) {
484 		printf("%-13s %*s %*s\n",
485 		    "Device:",
486 		    hlen, buf,
487 		    hlen, "Used:");
488 	}
489 
490 	total = used = tmp_total = tmp_used = 0;
491 	for (i = 0; i < n; ++i) {
492 		xsw = (void *)((char *)xswbuf + i * ksize);
493 
494 		if (xsw->xsw_nblks == 0)
495 			continue;
496 
497 		tmp_total = (long long)xsw->xsw_nblks * pagesize;
498 		tmp_used = (long long)xsw->xsw_used * pagesize;
499 		total += tmp_total;
500 		used += tmp_used;
501 		if (lflag) {
502 			sizetobuf(buf, sizeof(buf), hflag, tmp_total, hlen,
503 			    blocksize);
504 			if (xsw->xsw_dev == NODEV) {
505 				printf("%-13s %s ", "[NFS swap]", buf);
506 			} else {
507 				printf("/dev/%-8s %s ",
508 				    devname(xsw->xsw_dev, S_IFCHR), buf);
509 			}
510 
511 			sizetobuf(buf, sizeof(buf), hflag, tmp_used, hlen,
512 			    blocksize);
513 			printf("%s\n", buf);
514 		}
515 	}
516 
517 	if (sflag) {
518 		sizetobuf(buf, sizeof(buf), hflag, total, hlen, blocksize);
519 		printf("Total:        %s ", buf);
520 		sizetobuf(buf, sizeof(buf), hflag, used, hlen, blocksize);
521 		printf("%s\n", buf);
522 	}
523 }
524