xref: /dragonfly/sbin/swapon/swapon.c (revision 678e8cc6)
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. All advertising materials mentioning features or use of this software
14  *    must display the following acknowledgement:
15  *	This product includes software developed by the University of
16  *	California, Berkeley and its contributors.
17  * 4. Neither the name of the University nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  *
33  * @(#) Copyright (c) 1980, 1993 The Regents of the University of California.  All rights reserved.
34  * @(#)swapon.c	8.1 (Berkeley) 6/5/93
35  * $FreeBSD: src/sbin/swapon/swapon.c,v 1.8.2.2 2001/07/30 10:30:11 dd Exp $
36  */
37 
38 #include <sys/param.h>
39 #include <sys/stat.h>
40 #include <sys/sysctl.h>
41 #include <sys/diskslice.h>
42 #include <sys/ioctl_compat.h>
43 #include <vm/vm_param.h>
44 
45 #include <err.h>
46 #include <errno.h>
47 #include <fstab.h>
48 #include <stdio.h>
49 #include <stdlib.h>
50 #include <string.h>
51 #include <unistd.h>
52 #include <fcntl.h>
53 #include <libutil.h>
54 
55 static void usage(void);
56 static int swap_on_off(char *name, int doingall, int trim);
57 static void swaplist(int lflag, int sflag, int hflag);
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, qflag, eflag;
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 = qflag = doall = eflag = 0;
79 	while ((ch = getopt(argc, argv, "AadeghklmqsU")) != -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 'e':
102 			eflag = 1;
103 			break;
104 		case 'g':
105 			hflag = 'G';
106 			break;
107 		case 'h':
108 			hflag = 'H';
109 			break;
110 		case 'k':
111 			hflag = 'K';
112 			break;
113 		case 'l':
114 			lflag = 1;
115 			break;
116 		case 'm':
117 			hflag = 'M';
118 			break;
119 		case 'q':
120 			if (which_prog == SWAPON || which_prog == SWAPOFF)
121 				qflag = 1;
122 			break;
123 		case 's':
124 			sflag = 1;
125 			break;
126 		case 'U':
127 			if (which_prog == SWAPCTL) {
128 				doall = 1;
129 				which_prog = SWAPOFF;
130 			} else {
131 				usage();
132 			}
133 			break;
134 		case '?':
135 		default:
136 			usage();
137 		}
138 	}
139 	argv += optind;
140 
141 	ret = 0;
142 	if (which_prog == SWAPON || which_prog == SWAPOFF) {
143 		if (doall) {
144 			while ((fsp = getfsent()) != NULL) {
145 				if (strcmp(fsp->fs_type, FSTAB_SW))
146 					continue;
147 				if (strstr(fsp->fs_mntops, "noauto"))
148 					continue;
149 				if (swap_on_off(fsp->fs_spec, 1, eflag)) {
150 					ret = 1;
151 				} else {
152 					if (!qflag) {
153 						printf("%s: %sing %s as swap device\n",
154 						    getprogname(),
155 						    which_prog == SWAPOFF ? "remov" : "add",
156 						    fsp->fs_spec);
157 					}
158 				}
159 			}
160 		} else if (*argv == NULL) {
161 			usage();
162 		}
163 		for (; *argv; ++argv) {
164 			if (swap_on_off(getdevpath(*argv, 0), 0, eflag)) {
165 				ret = 1;
166 			} else if (orig_prog == SWAPCTL) {
167 				printf("%s: %sing %s as swap device\n",
168 				    getprogname(),
169 				    which_prog == SWAPOFF ? "remov" : "add",
170 				    *argv);
171 			}
172 		}
173 	} else {
174 		if (lflag || sflag)
175 			swaplist(lflag, sflag, hflag);
176 		else
177 			usage();
178 	}
179 	exit(ret);
180 }
181 
182 /*
183  * TRIM the device
184  */
185 static
186 void
187 trim_volume(char * name)
188 {
189 	struct partinfo pinfo;
190 	int fd,i,n;
191 	size_t bytes = 0,ksize;
192 	char *xswbuf;
193 	struct xswdev *xsw;
194 
195 
196 	/*
197 	* Determine if this device is already being used by swap without
198 	* calling swapon().
199 	*/
200 	if ((sysctlbyname("vm.swap_info_array", NULL, &bytes, NULL, 0) < 0) ||
201 	    bytes == 0) {
202 		err(1, "sysctlbyname()");
203 	}
204 
205 	xswbuf = malloc(bytes);
206 	if ((sysctlbyname("vm.swap_info_array", xswbuf, &bytes, NULL, 0) < 0) ||
207 	    bytes == 0) {
208 			free(xswbuf);
209 			err(1, "sysctlbyname()");
210 	}
211 
212 	ksize = ((struct xswdev *)xswbuf)->xsw_size;
213 	n = (int)(bytes / ksize);
214 	for (i = 0; i < n; ++i) {
215 		xsw = (void *)((char *)xswbuf + i * ksize);
216 
217 		if (xsw->xsw_dev == NODEV )
218 			continue;
219 		if(!strcmp(devname(xsw->xsw_dev, S_IFCHR),
220 		    name + strlen("/dev/"))) {
221 			warnx("%s: device already a swap device", name);
222 			exit(1);
223 		}
224 	}
225 
226 	/*
227 	 * Get the size and offset of this parititon/device
228 	 */
229 	fd = open(name, O_RDWR);
230 	if (fd < 0)
231 		err(1, "Unable to open %s R+W", name);
232 	if (ioctl(fd, DIOCGPART, &pinfo) < 0) {
233 		printf("Cannot trim regular file\n");
234 		usage ();
235 	}
236 	off_t ioarg[2];
237 
238 	/*Trim the Device*/
239 	ioarg[0] = pinfo.media_offset;
240 	ioarg[1] = pinfo.media_size;
241 	printf("Trimming Device:%s, sectors (%llu -%llu)\n",name,
242 	     (unsigned long long)ioarg[0]/512,
243 	     (unsigned long long)ioarg[1]/512);
244 	if (ioctl(fd, IOCTLTRIM, ioarg) < 0) {
245 		printf("Device trim failed\n");
246 		usage ();
247 	}
248 	close(fd);
249 }
250 
251 static int
252 swap_on_off(char *name, int doingall, int trim)
253 {
254 	if (which_prog == SWAPON && trim){
255 		char sysctl_name[64];
256 		int trim_enabled = 0;
257 		size_t olen = sizeof(trim_enabled);
258 		char *dev_name = strdup(name);
259 		dev_name = strtok(dev_name + strlen("/dev/da"),"s");
260 		sprintf(sysctl_name, "kern.cam.da.%s.trim_enabled", dev_name);
261 		sysctlbyname(sysctl_name, &trim_enabled, &olen, NULL, 0);
262 		if(errno == ENOENT) {
263 			printf("Device:%s does not support the TRIM command\n",
264 			    name);
265 			usage();
266 		}
267 		if(!trim_enabled) {
268 			printf("Erase device option selected, but sysctl (%s) "
269 			    "is not enabled\n",sysctl_name);
270 			usage();
271 		}
272 
273 		trim_volume(name);
274 
275 	}
276 	if ((which_prog == SWAPOFF ? swapoff(name) : swapon(name)) == -1) {
277 		switch(errno) {
278 		case EBUSY:
279 			if (!doingall)
280 				warnx("%s: device already in use", name);
281 			break;
282 		case EINVAL:
283 			if (which_prog == SWAPON)
284 				warnx("%s: NSWAPDEV limit reached", name);
285 			else if (!doingall)
286 				warn("%s", name);
287 			break;
288 		default:
289 			warn("%s", name);
290 			break;
291 		}
292 		return(1);
293 	}
294 	return(0);
295 }
296 
297 static void
298 usage(void)
299 {
300 	fprintf(stderr, "usage: %s ", getprogname());
301 	switch (orig_prog) {
302 	case SWAPON:
303 	case SWAPOFF:
304 		fprintf(stderr, "-aeq | file ...\n");
305 		break;
306 	case SWAPCTL:
307 		fprintf(stderr, "[-AeghklmsU] [-a file ... | -d file ...]\n");
308 		break;
309 	}
310 	exit(1);
311 }
312 
313 static void
314 sizetobuf(char *buf, size_t bufsize, int hflag, long long val, int hlen,
315     long blocksize)
316 {
317 	if (hflag == 'H') {
318 		char tmp[16];
319 
320 		humanize_number(tmp, 5, (int64_t)val, "", HN_AUTOSCALE,
321 		    HN_B | HN_NOSPACE | HN_DECIMAL);
322 		snprintf(buf, bufsize, "%*s", hlen, tmp);
323 	} else {
324 		snprintf(buf, bufsize, "%*lld", hlen, val / blocksize);
325 	}
326 }
327 
328 static void
329 swaplist(int lflag, int sflag, int hflag)
330 {
331 	size_t ksize, bytes = 0;
332 	char *xswbuf;
333 	struct xswdev *xsw;
334 	int hlen, pagesize;
335 	int i, n;
336 	long blocksize;
337 	long long total, used, tmp_total, tmp_used;
338 	char buf[32];
339 
340 	pagesize = getpagesize();
341 	switch(hflag) {
342 	case 'G':
343 		blocksize = 1024 * 1024 * 1024;
344 		strlcpy(buf, "1GB-blocks", sizeof(buf));
345 		hlen = 10;
346 		break;
347 	case 'H':
348 		blocksize = -1;
349 		strlcpy(buf, "Bytes", sizeof(buf));
350 		hlen = 10;
351 		break;
352 	case 'K':
353 		blocksize = 1024;
354 		strlcpy(buf, "1kB-blocks", sizeof(buf));
355 		hlen = 10;
356 		break;
357 	case 'M':
358 		blocksize = 1024 * 1024;
359 		strlcpy(buf, "1MB-blocks", sizeof(buf));
360 		hlen = 10;
361 		break;
362 	default:
363 		getbsize(&hlen, &blocksize);
364 		snprintf(buf, sizeof(buf), "%ld-blocks", blocksize);
365 		break;
366 	}
367 
368 	if (sysctlbyname("vm.swap_info_array", NULL, &bytes, NULL, 0) < 0)
369 		err(1, "sysctlbyname()");
370 	if (bytes == 0)
371 		err(1, "sysctlbyname()");
372 
373 	xswbuf = malloc(bytes);
374 	if (sysctlbyname("vm.swap_info_array", xswbuf, &bytes, NULL, 0) < 0) {
375 		free(xswbuf);
376 		err(1, "sysctlbyname()");
377 	}
378 	if (bytes == 0) {
379 		free(xswbuf);
380 		err(1, "sysctlbyname()");
381 	}
382 
383 	/*
384 	 * Calculate size of xsw entry returned by kernel (it can be larger
385 	 * than the one we have if there is a version mismatch).
386 	 */
387 	ksize = ((struct xswdev *)xswbuf)->xsw_size;
388 	n = (int)(bytes / ksize);
389 
390 	if (lflag) {
391 		printf("%-13s %*s %*s\n",
392 		    "Device:",
393 		    hlen, buf,
394 		    hlen, "Used:");
395 	}
396 
397 	total = used = tmp_total = tmp_used = 0;
398 	for (i = 0; i < n; ++i) {
399 		xsw = (void *)((char *)xswbuf + i * ksize);
400 
401 		if (xsw->xsw_nblks == 0)
402 			continue;
403 
404 		tmp_total = (long long)xsw->xsw_nblks * pagesize;
405 		tmp_used = (long long)xsw->xsw_used * pagesize;
406 		total += tmp_total;
407 		used += tmp_used;
408 		if (lflag) {
409 			sizetobuf(buf, sizeof(buf), hflag, tmp_total, hlen,
410 			    blocksize);
411 			if (xsw->xsw_dev == NODEV) {
412 				printf("%-13s %s ", "[NFS swap]", buf);
413 			} else {
414 				printf("/dev/%-8s %s ",
415 				    devname(xsw->xsw_dev, S_IFCHR), buf);
416 			}
417 
418 			sizetobuf(buf, sizeof(buf), hflag, tmp_used, hlen,
419 			    blocksize);
420 			printf("%s\n", buf);
421 		}
422 	}
423 
424 	if (sflag) {
425 		sizetobuf(buf, sizeof(buf), hflag, total, hlen, blocksize);
426 		printf("Total:        %s ", buf);
427 		sizetobuf(buf, sizeof(buf), hflag, used, hlen, blocksize);
428 		printf("%s\n", buf);
429 	}
430 }
431