xref: /dragonfly/sbin/swapon/swapon.c (revision 7bcb6caf)
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, "AacdeghiklmqsU")) != -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 			eflag = 1;
107 			break;
108 		case 'g':
109 			hflag = 'G';
110 			break;
111 		case 'h':
112 			hflag = 'H';
113 			break;
114 		case 'i':
115 			iflag = 1;
116 			break;
117 		case 'k':
118 			hflag = 'K';
119 			break;
120 		case 'l':
121 			lflag = 1;
122 			break;
123 		case 'm':
124 			hflag = 'M';
125 			break;
126 		case 'q':
127 			if (which_prog == SWAPON || which_prog == SWAPOFF)
128 				qflag = 1;
129 			break;
130 		case 's':
131 			sflag = 1;
132 			break;
133 		case 'U':
134 			if (which_prog == SWAPCTL) {
135 				doall = 1;
136 				which_prog = SWAPOFF;
137 			} else {
138 				usage();
139 			}
140 			break;
141 		case '?':
142 		default:
143 			usage();
144 		}
145 	}
146 	argv += optind;
147 
148 	ret = 0;
149 	if (which_prog == SWAPON || which_prog == SWAPOFF) {
150 		if (doall) {
151 			while ((fsp = getfsent()) != NULL) {
152 				char *fs_spec;
153 				int dotrim = eflag;
154 
155 				if (strcmp(fsp->fs_type, FSTAB_SW))
156 					continue;
157 				if (strstr(fsp->fs_mntops, "noauto"))
158 					continue;
159 
160 				if (strstr(fsp->fs_mntops, "notrim"))
161 					dotrim = 0;
162 				else if (strstr(fsp->fs_mntops, "trim"))
163 					dotrim = 1;
164 
165 				if (cflag || strstr(fsp->fs_mntops, "crypt"))
166 					fs_spec = docrypt(fsp->fs_spec, 1);
167 				else
168 					fs_spec = strdup(fsp->fs_spec);
169 				if (swap_on_off(fs_spec, 1, dotrim, iflag)) {
170 					ret = 1;
171 				} else {
172 					if (cflag ||
173 					    strstr(fsp->fs_mntops, "crypt")) {
174 						docrypt(fsp->fs_spec, 2);
175 					}
176 					if (!qflag) {
177 						printf("%s: %sing %s as swap "
178 						       "device\n",
179 						    getprogname(),
180 						    (which_prog == SWAPOFF ?
181 							    "remov" : "add"),
182 						    fs_spec);
183 					}
184 				}
185 				free(fs_spec);
186 			}
187 		} else if (*argv == NULL) {
188 			usage();
189 		}
190 		for (; *argv; ++argv) {
191 			char *ospec = getdevpath(*argv, 0);
192 			char *fs_spec;
193 			if (cflag)
194 				fs_spec = docrypt(ospec, 1);
195 			else
196 				fs_spec = strdup(ospec);
197 			if (swap_on_off(fs_spec, 0, eflag, iflag)) {
198 				ret = 1;
199 			} else {
200 				if (cflag)
201 					docrypt(ospec, 2);
202 				if (!qflag) {
203 					printf("%s: %sing %s as swap device\n",
204 					    getprogname(),
205 					    (which_prog == SWAPOFF ?
206 						"remov" : "add"),
207 					    fs_spec);
208 				}
209 			}
210 			free(fs_spec);
211 		}
212 	} else {
213 		if (lflag || sflag)
214 			swaplist(lflag, sflag, hflag);
215 		else
216 			usage();
217 	}
218 	exit(ret);
219 }
220 
221 static
222 char *
223 docrypt(char *fs_spec, int pass)
224 {
225 	char *id;
226 	char *res;
227 	char *buf;
228 
229 	if ((id = strrchr(fs_spec, '/')) == NULL)
230 		id = fs_spec;
231 	else
232 		++id;
233 	asprintf(&id, "swap-%s", id);
234 	asprintf(&res, "/dev/mapper/%s", id);
235 
236 	switch(which_prog) {
237 	case SWAPOFF:
238 		if (pass != 2)
239 			break;
240 		asprintf(&buf, "/sbin/cryptsetup remove %s", id);
241 		system(buf);
242 		free(buf);
243 		free(id);
244 		break;
245 	case SWAPON:
246 		if (pass != 1)
247 			break;
248 		if (kldfind("dm_target_crypt") < 0)
249 			kldload("dm_target_crypt");
250 
251 		asprintf(&buf,
252 			 "/sbin/cryptsetup --key-file /dev/urandom "
253 			 "--key-size 256 create %s %s",
254 			 id, fs_spec);
255 		if (qflag == 0)
256 			printf("%s\n", buf);
257 		system(buf);
258 		free(buf);
259 		free(id);
260 
261 		/*
262 		 * NOTE: Don't revert to /dev/da* on error because this could
263 		 *	 inadvertently add both /dev/da* and
264 		 *	 /dev/mapper/swap-da*.
265 		 *
266 		 *	 Allow the swapon operation to report failure or
267 		 *	 report a duplicate.
268 		 */
269 		break;
270 	default:
271 		free(res);
272 		free(id);
273 		res = strdup(fs_spec);
274 		break;
275 	}
276 
277 	if (pass == 2) {
278 		free (res);
279 		res = NULL;
280 	}
281 	return res;
282 }
283 
284 /*
285  * TRIM the device
286  */
287 static
288 void
289 trim_volume(char * name)
290 {
291 	struct partinfo pinfo;
292 	int fd,i,n;
293 	size_t bytes = 0,ksize;
294 	char *xswbuf;
295 	struct xswdev *xsw;
296 
297 
298 	/*
299 	* Determine if this device is already being used by swap without
300 	* calling swapon().
301 	*/
302 	if ((sysctlbyname("vm.swap_info_array", NULL, &bytes, NULL, 0) < 0) ||
303 	    bytes == 0) {
304 		err(1, "sysctlbyname()");
305 	}
306 
307 	xswbuf = malloc(bytes);
308 	if ((sysctlbyname("vm.swap_info_array", xswbuf, &bytes, NULL, 0) < 0) ||
309 	    bytes == 0) {
310 			free(xswbuf);
311 			err(1, "sysctlbyname()");
312 	}
313 
314 	ksize = ((struct xswdev *)xswbuf)->xsw_size;
315 	n = (int)(bytes / ksize);
316 	for (i = 0; i < n; ++i) {
317 		xsw = (void *)((char *)xswbuf + i * ksize);
318 
319 		if (xsw->xsw_dev == NODEV )
320 			continue;
321 		if(!strcmp(devname(xsw->xsw_dev, S_IFCHR),
322 		    name + strlen("/dev/"))) {
323 			warnx("%s: device already a swap device", name);
324 			exit(1);
325 		}
326 	}
327 
328 	/*
329 	 * Get the size and offset of this parititon/device
330 	 */
331 	fd = open(name, O_RDWR);
332 	if (fd < 0)
333 		err(1, "Unable to open %s R+W", name);
334 	if (ioctl(fd, DIOCGPART, &pinfo) < 0) {
335 		printf("Cannot trim regular file\n");
336 		usage ();
337 	}
338 	off_t ioarg[2];
339 
340 	/*Trim the Device*/
341 	ioarg[0] = pinfo.media_offset;
342 	ioarg[1] = pinfo.media_size;
343 	printf("Trimming Device:%s, sectors (%llu -%llu)\n",name,
344 	     (unsigned long long)ioarg[0]/512,
345 	     (unsigned long long)ioarg[1]/512);
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 		char sysctl_name[64];
369 		int trim_enabled = 0;
370 		size_t olen = sizeof(trim_enabled);
371 		char *dev_name = strdup(name);
372 		dev_name = strtok(dev_name + strlen("/dev/da"),"s");
373 		sprintf(sysctl_name, "kern.cam.da.%s.trim_enabled", dev_name);
374 		if (sysctlbyname(sysctl_name, &trim_enabled, &olen, NULL, 0) < 0) {
375 			if (qflag == 0) {
376 				printf("TRIM not supported on %s, "
377 				       "ignoring\n",
378 				       name);
379 			}
380 			errno = 0;
381 		} else if (!trim_enabled) {
382 			if (qflag == 0) {
383 				printf("TRIM not enabled on %s (%s), "
384 				       "ignoring\n",
385 				       name, sysctl_name);
386 			}
387 		} else {
388 			trim_volume(name);
389 		}
390 	}
391 	if ((which_prog == SWAPOFF ? swapoff(name) : swapon(name)) == -1) {
392 		switch(errno) {
393 		case EBUSY:
394 			if (!doingall)
395 				warnx("%s: device already in use", name);
396 			break;
397 		case EINVAL:
398 			if (which_prog == SWAPON)
399 				warnx("%s: NSWAPDEV limit reached", name);
400 			else if (!doingall)
401 				warn("%s", name);
402 			break;
403 		default:
404 			warn("%s", name);
405 			break;
406 		}
407 		return(1);
408 	}
409 	return(0);
410 }
411 
412 static void
413 usage(void)
414 {
415 	fprintf(stderr, "usage: %s ", getprogname());
416 	switch (orig_prog) {
417 	case SWAPON:
418 	case SWAPOFF:
419 		fprintf(stderr, "-aeiq | file ...\n");
420 		break;
421 	case SWAPCTL:
422 		fprintf(stderr, "[-AeghiklmsU] [-a file ... | -d file ...]\n");
423 		break;
424 	}
425 	exit(1);
426 }
427 
428 static void
429 sizetobuf(char *buf, size_t bufsize, int hflag, long long val, int hlen,
430     long blocksize)
431 {
432 	if (hflag == 'H') {
433 		char tmp[16];
434 
435 		humanize_number(tmp, 5, (int64_t)val, "", HN_AUTOSCALE,
436 		    HN_B | HN_NOSPACE | HN_DECIMAL);
437 		snprintf(buf, bufsize, "%*s", hlen, tmp);
438 	} else {
439 		snprintf(buf, bufsize, "%*lld", hlen, val / blocksize);
440 	}
441 }
442 
443 static void
444 swaplist(int lflag, int sflag, int hflag)
445 {
446 	size_t ksize, bytes = 0;
447 	char *xswbuf;
448 	struct xswdev *xsw;
449 	int hlen, pagesize;
450 	int i, n;
451 	long blocksize;
452 	long long total, used, tmp_total, tmp_used;
453 	char buf[32];
454 
455 	pagesize = getpagesize();
456 	switch(hflag) {
457 	case 'G':
458 		blocksize = 1024 * 1024 * 1024;
459 		strlcpy(buf, "1GB-blocks", sizeof(buf));
460 		hlen = 10;
461 		break;
462 	case 'H':
463 		blocksize = -1;
464 		strlcpy(buf, "Bytes", sizeof(buf));
465 		hlen = 10;
466 		break;
467 	case 'K':
468 		blocksize = 1024;
469 		strlcpy(buf, "1kB-blocks", sizeof(buf));
470 		hlen = 10;
471 		break;
472 	case 'M':
473 		blocksize = 1024 * 1024;
474 		strlcpy(buf, "1MB-blocks", sizeof(buf));
475 		hlen = 10;
476 		break;
477 	default:
478 		getbsize(&hlen, &blocksize);
479 		snprintf(buf, sizeof(buf), "%ld-blocks", blocksize);
480 		break;
481 	}
482 
483 	if (sysctlbyname("vm.swap_info_array", NULL, &bytes, NULL, 0) < 0)
484 		err(1, "sysctlbyname()");
485 	if (bytes == 0)
486 		err(1, "sysctlbyname()");
487 
488 	xswbuf = malloc(bytes);
489 	if (sysctlbyname("vm.swap_info_array", xswbuf, &bytes, NULL, 0) < 0) {
490 		free(xswbuf);
491 		err(1, "sysctlbyname()");
492 	}
493 	if (bytes == 0) {
494 		free(xswbuf);
495 		err(1, "sysctlbyname()");
496 	}
497 
498 	/*
499 	 * Calculate size of xsw entry returned by kernel (it can be larger
500 	 * than the one we have if there is a version mismatch).
501 	 */
502 	ksize = ((struct xswdev *)xswbuf)->xsw_size;
503 	n = (int)(bytes / ksize);
504 
505 	if (lflag) {
506 		printf("%-13s %*s %*s\n",
507 		    "Device:",
508 		    hlen, buf,
509 		    hlen, "Used:");
510 	}
511 
512 	total = used = tmp_total = tmp_used = 0;
513 	for (i = 0; i < n; ++i) {
514 		xsw = (void *)((char *)xswbuf + i * ksize);
515 
516 		if (xsw->xsw_nblks == 0)
517 			continue;
518 
519 		tmp_total = (long long)xsw->xsw_nblks * pagesize;
520 		tmp_used = (long long)xsw->xsw_used * pagesize;
521 		total += tmp_total;
522 		used += tmp_used;
523 		if (lflag) {
524 			sizetobuf(buf, sizeof(buf), hflag, tmp_total, hlen,
525 			    blocksize);
526 			if (xsw->xsw_dev == NODEV) {
527 				printf("%-13s %s ", "[NFS swap]", buf);
528 			} else {
529 				printf("/dev/%-8s %s ",
530 				    devname(xsw->xsw_dev, S_IFCHR), buf);
531 			}
532 
533 			sizetobuf(buf, sizeof(buf), hflag, tmp_used, hlen,
534 			    blocksize);
535 			printf("%s\n", buf);
536 		}
537 	}
538 
539 	if (sflag) {
540 		sizetobuf(buf, sizeof(buf), hflag, total, hlen, blocksize);
541 		printf("Total:        %s ", buf);
542 		sizetobuf(buf, sizeof(buf), hflag, used, hlen, blocksize);
543 		printf("%s\n", buf);
544 	}
545 }
546