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