xref: /dragonfly/sbin/swapon/swapon.c (revision e65bc1c3)
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/diskslice.h>
38 #include <sys/ioctl_compat.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 static void usage(void);
52 static int swap_on_off(char *name, int doingall, int trim);
53 static void swaplist(int lflag, int sflag, int hflag);
54 
55 enum { SWAPON, SWAPOFF, SWAPCTL } orig_prog, which_prog = SWAPCTL;
56 
57 int
58 main(int argc, char **argv)
59 {
60 	struct fstab *fsp;
61 	char *ptr;
62 	int ret;
63 	int ch;
64 	int doall, sflag, lflag, hflag, qflag, eflag;
65 
66 	if ((ptr = strrchr(argv[0], '/')) == NULL)
67 		ptr = argv[0];
68 	if (strstr(ptr, "swapon"))
69 		which_prog = SWAPON;
70 	else if (strstr(ptr, "swapoff"))
71 		which_prog = SWAPOFF;
72 	orig_prog = which_prog;
73 
74 	sflag = lflag = hflag = qflag = doall = eflag = 0;
75 	while ((ch = getopt(argc, argv, "AadeghklmqsU")) != -1) {
76 		switch((char)ch) {
77 		case 'A':
78 			if (which_prog == SWAPCTL) {
79 				doall = 1;
80 				which_prog = SWAPON;
81 			} else {
82 				usage();
83 			}
84 			break;
85 		case 'a':
86 			if (which_prog == SWAPON || which_prog == SWAPOFF)
87 				doall = 1;
88 			else
89 				which_prog = SWAPON;
90 			break;
91 		case 'd':
92 			if (which_prog == SWAPCTL)
93 				which_prog = SWAPOFF;
94 			else
95 				usage();
96 			break;
97 		case 'e':
98 			eflag = 1;
99 			break;
100 		case 'g':
101 			hflag = 'G';
102 			break;
103 		case 'h':
104 			hflag = 'H';
105 			break;
106 		case 'k':
107 			hflag = 'K';
108 			break;
109 		case 'l':
110 			lflag = 1;
111 			break;
112 		case 'm':
113 			hflag = 'M';
114 			break;
115 		case 'q':
116 			if (which_prog == SWAPON || which_prog == SWAPOFF)
117 				qflag = 1;
118 			break;
119 		case 's':
120 			sflag = 1;
121 			break;
122 		case 'U':
123 			if (which_prog == SWAPCTL) {
124 				doall = 1;
125 				which_prog = SWAPOFF;
126 			} else {
127 				usage();
128 			}
129 			break;
130 		case '?':
131 		default:
132 			usage();
133 		}
134 	}
135 	argv += optind;
136 
137 	ret = 0;
138 	if (which_prog == SWAPON || which_prog == SWAPOFF) {
139 		if (doall) {
140 			while ((fsp = getfsent()) != NULL) {
141 				if (strcmp(fsp->fs_type, FSTAB_SW))
142 					continue;
143 				if (strstr(fsp->fs_mntops, "noauto"))
144 					continue;
145 				if (swap_on_off(fsp->fs_spec, 1, eflag)) {
146 					ret = 1;
147 				} else {
148 					if (!qflag) {
149 						printf("%s: %sing %s as swap device\n",
150 						    getprogname(),
151 						    which_prog == SWAPOFF ? "remov" : "add",
152 						    fsp->fs_spec);
153 					}
154 				}
155 			}
156 		} else if (*argv == NULL) {
157 			usage();
158 		}
159 		for (; *argv; ++argv) {
160 			if (swap_on_off(getdevpath(*argv, 0), 0, eflag)) {
161 				ret = 1;
162 			} else if (orig_prog == SWAPCTL) {
163 				printf("%s: %sing %s as swap device\n",
164 				    getprogname(),
165 				    which_prog == SWAPOFF ? "remov" : "add",
166 				    *argv);
167 			}
168 		}
169 	} else {
170 		if (lflag || sflag)
171 			swaplist(lflag, sflag, hflag);
172 		else
173 			usage();
174 	}
175 	exit(ret);
176 }
177 
178 /*
179  * TRIM the device
180  */
181 static
182 void
183 trim_volume(char * name)
184 {
185 	struct partinfo pinfo;
186 	int fd,i,n;
187 	size_t bytes = 0,ksize;
188 	char *xswbuf;
189 	struct xswdev *xsw;
190 
191 
192 	/*
193 	* Determine if this device is already being used by swap without
194 	* calling swapon().
195 	*/
196 	if ((sysctlbyname("vm.swap_info_array", NULL, &bytes, NULL, 0) < 0) ||
197 	    bytes == 0) {
198 		err(1, "sysctlbyname()");
199 	}
200 
201 	xswbuf = malloc(bytes);
202 	if ((sysctlbyname("vm.swap_info_array", xswbuf, &bytes, NULL, 0) < 0) ||
203 	    bytes == 0) {
204 			free(xswbuf);
205 			err(1, "sysctlbyname()");
206 	}
207 
208 	ksize = ((struct xswdev *)xswbuf)->xsw_size;
209 	n = (int)(bytes / ksize);
210 	for (i = 0; i < n; ++i) {
211 		xsw = (void *)((char *)xswbuf + i * ksize);
212 
213 		if (xsw->xsw_dev == NODEV )
214 			continue;
215 		if(!strcmp(devname(xsw->xsw_dev, S_IFCHR),
216 		    name + strlen("/dev/"))) {
217 			warnx("%s: device already a swap device", name);
218 			exit(1);
219 		}
220 	}
221 
222 	/*
223 	 * Get the size and offset of this parititon/device
224 	 */
225 	fd = open(name, O_RDWR);
226 	if (fd < 0)
227 		err(1, "Unable to open %s R+W", name);
228 	if (ioctl(fd, DIOCGPART, &pinfo) < 0) {
229 		printf("Cannot trim regular file\n");
230 		usage ();
231 	}
232 	off_t ioarg[2];
233 
234 	/*Trim the Device*/
235 	ioarg[0] = pinfo.media_offset;
236 	ioarg[1] = pinfo.media_size;
237 	printf("Trimming Device:%s, sectors (%llu -%llu)\n",name,
238 	     (unsigned long long)ioarg[0]/512,
239 	     (unsigned long long)ioarg[1]/512);
240 	if (ioctl(fd, IOCTLTRIM, ioarg) < 0) {
241 		printf("Device trim failed\n");
242 		usage ();
243 	}
244 	close(fd);
245 }
246 
247 static int
248 swap_on_off(char *name, int doingall, int trim)
249 {
250 	if (which_prog == SWAPON && trim){
251 		char sysctl_name[64];
252 		int trim_enabled = 0;
253 		size_t olen = sizeof(trim_enabled);
254 		char *dev_name = strdup(name);
255 		dev_name = strtok(dev_name + strlen("/dev/da"),"s");
256 		sprintf(sysctl_name, "kern.cam.da.%s.trim_enabled", dev_name);
257 		sysctlbyname(sysctl_name, &trim_enabled, &olen, NULL, 0);
258 		if(errno == ENOENT) {
259 			printf("Device:%s does not support the TRIM command\n",
260 			    name);
261 			usage();
262 		}
263 		if(!trim_enabled) {
264 			printf("Erase device option selected, but sysctl (%s) "
265 			    "is not enabled\n",sysctl_name);
266 			usage();
267 		}
268 
269 		trim_volume(name);
270 
271 	}
272 	if ((which_prog == SWAPOFF ? swapoff(name) : swapon(name)) == -1) {
273 		switch(errno) {
274 		case EBUSY:
275 			if (!doingall)
276 				warnx("%s: device already in use", name);
277 			break;
278 		case EINVAL:
279 			if (which_prog == SWAPON)
280 				warnx("%s: NSWAPDEV limit reached", name);
281 			else if (!doingall)
282 				warn("%s", name);
283 			break;
284 		default:
285 			warn("%s", name);
286 			break;
287 		}
288 		return(1);
289 	}
290 	return(0);
291 }
292 
293 static void
294 usage(void)
295 {
296 	fprintf(stderr, "usage: %s ", getprogname());
297 	switch (orig_prog) {
298 	case SWAPON:
299 	case SWAPOFF:
300 		fprintf(stderr, "-aeq | file ...\n");
301 		break;
302 	case SWAPCTL:
303 		fprintf(stderr, "[-AeghklmsU] [-a file ... | -d file ...]\n");
304 		break;
305 	}
306 	exit(1);
307 }
308 
309 static void
310 sizetobuf(char *buf, size_t bufsize, int hflag, long long val, int hlen,
311     long blocksize)
312 {
313 	if (hflag == 'H') {
314 		char tmp[16];
315 
316 		humanize_number(tmp, 5, (int64_t)val, "", HN_AUTOSCALE,
317 		    HN_B | HN_NOSPACE | HN_DECIMAL);
318 		snprintf(buf, bufsize, "%*s", hlen, tmp);
319 	} else {
320 		snprintf(buf, bufsize, "%*lld", hlen, val / blocksize);
321 	}
322 }
323 
324 static void
325 swaplist(int lflag, int sflag, int hflag)
326 {
327 	size_t ksize, bytes = 0;
328 	char *xswbuf;
329 	struct xswdev *xsw;
330 	int hlen, pagesize;
331 	int i, n;
332 	long blocksize;
333 	long long total, used, tmp_total, tmp_used;
334 	char buf[32];
335 
336 	pagesize = getpagesize();
337 	switch(hflag) {
338 	case 'G':
339 		blocksize = 1024 * 1024 * 1024;
340 		strlcpy(buf, "1GB-blocks", sizeof(buf));
341 		hlen = 10;
342 		break;
343 	case 'H':
344 		blocksize = -1;
345 		strlcpy(buf, "Bytes", sizeof(buf));
346 		hlen = 10;
347 		break;
348 	case 'K':
349 		blocksize = 1024;
350 		strlcpy(buf, "1kB-blocks", sizeof(buf));
351 		hlen = 10;
352 		break;
353 	case 'M':
354 		blocksize = 1024 * 1024;
355 		strlcpy(buf, "1MB-blocks", sizeof(buf));
356 		hlen = 10;
357 		break;
358 	default:
359 		getbsize(&hlen, &blocksize);
360 		snprintf(buf, sizeof(buf), "%ld-blocks", blocksize);
361 		break;
362 	}
363 
364 	if (sysctlbyname("vm.swap_info_array", NULL, &bytes, NULL, 0) < 0)
365 		err(1, "sysctlbyname()");
366 	if (bytes == 0)
367 		err(1, "sysctlbyname()");
368 
369 	xswbuf = malloc(bytes);
370 	if (sysctlbyname("vm.swap_info_array", xswbuf, &bytes, NULL, 0) < 0) {
371 		free(xswbuf);
372 		err(1, "sysctlbyname()");
373 	}
374 	if (bytes == 0) {
375 		free(xswbuf);
376 		err(1, "sysctlbyname()");
377 	}
378 
379 	/*
380 	 * Calculate size of xsw entry returned by kernel (it can be larger
381 	 * than the one we have if there is a version mismatch).
382 	 */
383 	ksize = ((struct xswdev *)xswbuf)->xsw_size;
384 	n = (int)(bytes / ksize);
385 
386 	if (lflag) {
387 		printf("%-13s %*s %*s\n",
388 		    "Device:",
389 		    hlen, buf,
390 		    hlen, "Used:");
391 	}
392 
393 	total = used = tmp_total = tmp_used = 0;
394 	for (i = 0; i < n; ++i) {
395 		xsw = (void *)((char *)xswbuf + i * ksize);
396 
397 		if (xsw->xsw_nblks == 0)
398 			continue;
399 
400 		tmp_total = (long long)xsw->xsw_nblks * pagesize;
401 		tmp_used = (long long)xsw->xsw_used * pagesize;
402 		total += tmp_total;
403 		used += tmp_used;
404 		if (lflag) {
405 			sizetobuf(buf, sizeof(buf), hflag, tmp_total, hlen,
406 			    blocksize);
407 			if (xsw->xsw_dev == NODEV) {
408 				printf("%-13s %s ", "[NFS swap]", buf);
409 			} else {
410 				printf("/dev/%-8s %s ",
411 				    devname(xsw->xsw_dev, S_IFCHR), buf);
412 			}
413 
414 			sizetobuf(buf, sizeof(buf), hflag, tmp_used, hlen,
415 			    blocksize);
416 			printf("%s\n", buf);
417 		}
418 	}
419 
420 	if (sflag) {
421 		sizetobuf(buf, sizeof(buf), hflag, total, hlen, blocksize);
422 		printf("Total:        %s ", buf);
423 		sizetobuf(buf, sizeof(buf), hflag, used, hlen, blocksize);
424 		printf("%s\n", buf);
425 	}
426 }
427