xref: /freebsd/sbin/swapon/swapon.c (revision 7fdf597e96a02165cfe22ff357b857d5fa15ed8a)
1 /*-
2  * SPDX-License-Identifier: BSD-3-Clause
3  *
4  * Copyright (c) 1980, 1993
5  *	The Regents of the University of California.  All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. Neither the name of the University nor the names of its contributors
16  *    may be used to endorse or promote products derived from this software
17  *    without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  */
31 
32 #include <sys/param.h>
33 #include <sys/disk.h>
34 #include <sys/disklabel.h>
35 #include <sys/mdioctl.h>
36 #include <sys/stat.h>
37 #include <sys/sysctl.h>
38 #include <sys/wait.h>
39 #include <vm/vm_param.h>
40 #include <vm/swap_pager.h>
41 
42 #include <err.h>
43 #include <errno.h>
44 #include <fcntl.h>
45 #include <fnmatch.h>
46 #include <fstab.h>
47 #include <libgen.h>
48 #include <libutil.h>
49 #include <limits.h>
50 #include <paths.h>
51 #include <stdarg.h>
52 #include <stdio.h>
53 #include <stdlib.h>
54 #include <string.h>
55 #include <unistd.h>
56 
57 static void usage(void) __dead2;
58 static const char *swap_on_off(const char *, int, char *);
59 static const char *swap_on_off_geli(const char *, char *, int);
60 static const char *swap_on_off_md(const char *, char *, int);
61 static const char *swap_on_off_sfile(const char *, int);
62 static void swaplist(int, int, int);
63 static int run_cmd(int *, const char *, ...) __printflike(2, 3);
64 
65 static enum { SWAPON, SWAPOFF, SWAPCTL } orig_prog, which_prog = SWAPCTL;
66 
67 static int Eflag, fflag, qflag;
68 
69 int
70 main(int argc, char **argv)
71 {
72 	struct fstab *fsp;
73 	const char *swfile;
74 	char *ptr;
75 	int ret, ch, doall;
76 	int sflag, lflag, late, hflag;
77 	const char *etc_fstab;
78 
79 	sflag = lflag = late = hflag = 0;
80 	if ((ptr = strrchr(argv[0], '/')) == NULL)
81 		ptr = argv[0];
82 	if (strstr(ptr, "swapon") != NULL)
83 		which_prog = SWAPON;
84 	else if (strstr(ptr, "swapoff") != NULL)
85 		which_prog = SWAPOFF;
86 	orig_prog = which_prog;
87 
88 	doall = 0;
89 	etc_fstab = NULL;
90 	while ((ch = getopt(argc, argv, "AadEfghklLmqsUF:")) != -1) {
91 		switch(ch) {
92 		case 'A':
93 			if (which_prog == SWAPCTL) {
94 				doall = 1;
95 				which_prog = SWAPON;
96 			} else
97 				usage();
98 			break;
99 		case 'a':
100 			if (which_prog == SWAPON || which_prog == SWAPOFF)
101 				doall = 1;
102 			else
103 				which_prog = SWAPON;
104 			break;
105 		case 'd':
106 			if (which_prog == SWAPCTL)
107 				which_prog = SWAPOFF;
108 			else
109 				usage();
110 			break;
111 		case 'E':
112 			if (which_prog == SWAPON)
113 				Eflag = 2;
114 			else
115 				usage();
116 			break;
117 		case 'f':
118 			if (which_prog == SWAPOFF)
119 				fflag = 1;
120 			else
121 				usage();
122 			break;
123 		case 'g':
124 			hflag = 'G';
125 			break;
126 		case 'h':
127 			hflag = 'H';
128 			break;
129 		case 'k':
130 			hflag = 'K';
131 			break;
132 		case 'l':
133 			lflag = 1;
134 			break;
135 		case 'L':
136 			late = 1;
137 			break;
138 		case 'm':
139 			hflag = 'M';
140 			break;
141 		case 'q':
142 			if (which_prog == SWAPON || which_prog == SWAPOFF)
143 				qflag = 1;
144 			break;
145 		case 's':
146 			sflag = 1;
147 			break;
148 		case 'U':
149 			if (which_prog == SWAPCTL) {
150 				doall = 1;
151 				which_prog = SWAPOFF;
152 			} else
153 				usage();
154 			break;
155 		case 'F':
156 			etc_fstab = optarg;
157 			break;
158 		case '?':
159 		default:
160 			usage();
161 		}
162 	}
163 	argv += optind;
164 
165 	ret = 0;
166 	swfile = NULL;
167 	if (etc_fstab != NULL)
168 		setfstab(etc_fstab);
169 	if (which_prog == SWAPON || which_prog == SWAPOFF) {
170 		if (doall) {
171 			while ((fsp = getfsent()) != NULL) {
172 				if (strcmp(fsp->fs_type, FSTAB_SW) != 0)
173 					continue;
174 				if (strstr(fsp->fs_mntops, "noauto") != NULL)
175 					continue;
176 				if (which_prog != SWAPOFF &&
177 				    strstr(fsp->fs_mntops, "late") &&
178 				    late == 0)
179 					continue;
180 				if (which_prog == SWAPOFF &&
181 				    strstr(fsp->fs_mntops, "late") == NULL &&
182 				    late != 0)
183 					continue;
184 				Eflag |= (strstr(fsp->fs_mntops, "trimonce") != NULL);
185 				swfile = swap_on_off(fsp->fs_spec, 1,
186 				    fsp->fs_mntops);
187 				Eflag &= ~1;
188 				if (swfile == NULL) {
189 					ret = 1;
190 					continue;
191 				}
192 				if (qflag == 0) {
193 					printf("%s: %sing %s as swap device\n",
194 					    getprogname(),
195 					    (which_prog == SWAPOFF) ?
196 					    "remov" : "add", swfile);
197 				}
198 			}
199 		} else if (*argv == NULL)
200 			usage();
201 		for (; *argv; ++argv) {
202 			swfile = swap_on_off(*argv, 0, NULL);
203 			if (swfile == NULL) {
204 				ret = 1;
205 				continue;
206 			}
207 			if (orig_prog == SWAPCTL) {
208 				printf("%s: %sing %s as swap device\n",
209 				    getprogname(),
210 				    (which_prog == SWAPOFF) ? "remov" : "add",
211 				    swfile);
212 			}
213 		}
214 	} else {
215 		if (lflag || sflag)
216 			swaplist(lflag, sflag, hflag);
217 		else
218 			usage();
219 	}
220 	exit(ret);
221 }
222 
223 static const char *
224 swap_on_off(const char *name, int doingall, char *mntops)
225 {
226 	char *base, *basebuf;
227 
228 	/* Swap on vnode-backed md(4) device. */
229 	if (mntops != NULL &&
230 	    (fnmatch(_PATH_DEV MD_NAME "[0-9]*", name, 0) == 0 ||
231 	     fnmatch(MD_NAME "[0-9]*", name, 0) == 0 ||
232 	     strncmp(_PATH_DEV MD_NAME, name,
233 		sizeof(_PATH_DEV) + sizeof(MD_NAME)) == 0 ||
234 	     strncmp(MD_NAME, name, sizeof(MD_NAME)) == 0))
235 		return (swap_on_off_md(name, mntops, doingall));
236 
237 	basebuf = strdup(name);
238 	base = basename(basebuf);
239 
240 	/* Swap on encrypted device by GEOM_ELI. */
241 	if (fnmatch("*.eli", base, 0) == 0) {
242 		free(basebuf);
243 		return (swap_on_off_geli(name, mntops, doingall));
244 	}
245 
246 	/* Swap on special file. */
247 	free(basebuf);
248 	return (swap_on_off_sfile(name, doingall));
249 }
250 
251 /* Strip off .bde or .eli suffix from swap device name */
252 static char *
253 swap_basename(const char *name)
254 {
255 	char *dname, *p;
256 
257 	dname = strdup(name);
258 	p = strrchr(dname, '.');
259 	/* assert(p != NULL); */
260 	*p = '\0';
261 
262 	return (dname);
263 }
264 
265 /* Build geli(8) arguments from mntops */
266 static char *
267 swap_on_geli_args(const char *mntops)
268 {
269 	const char *aalgo, *ealgo, *keylen_str, *sectorsize_str;
270 	const char *aflag, *eflag, *lflag, *Tflag, *sflag;
271 	char *p, *args, *token, *string, *ops;
272 	int pagesize;
273 	size_t pagesize_len;
274 	u_long ul;
275 
276 	/* Use built-in defaults for geli(8). */
277 	aalgo = ealgo = keylen_str = "";
278 	aflag = eflag = lflag = Tflag = "";
279 
280 	/* We will always specify sectorsize. */
281 	sflag = " -s ";
282 	sectorsize_str = NULL;
283 
284 	if (mntops != NULL) {
285 		string = ops = strdup(mntops);
286 
287 		while ((token = strsep(&string, ",")) != NULL) {
288 			if ((p = strstr(token, "aalgo=")) == token) {
289 				aalgo = p + sizeof("aalgo=") - 1;
290 				aflag = " -a ";
291 			} else if ((p = strstr(token, "ealgo=")) == token) {
292 				ealgo = p + sizeof("ealgo=") - 1;
293 				eflag = " -e ";
294 			} else if ((p = strstr(token, "keylen=")) == token) {
295 				keylen_str = p + sizeof("keylen=") - 1;
296 				errno = 0;
297 				ul = strtoul(keylen_str, &p, 10);
298 				if (errno == 0) {
299 					if (*p != '\0' || ul > INT_MAX)
300 						errno = EINVAL;
301 				}
302 				if (errno) {
303 					warn("Invalid keylen: %s", keylen_str);
304 					free(ops);
305 					return (NULL);
306 				}
307 				lflag = " -l ";
308 			} else if ((p = strstr(token, "sectorsize=")) == token) {
309 				sectorsize_str = p + sizeof("sectorsize=") - 1;
310 				errno = 0;
311 				ul = strtoul(sectorsize_str, &p, 10);
312 				if (errno == 0) {
313 					if (*p != '\0' || ul > INT_MAX)
314 						errno = EINVAL;
315 				}
316 				if (errno) {
317 					warn("Invalid sectorsize: %s",
318 					    sectorsize_str);
319 					free(ops);
320 					return (NULL);
321 				}
322 			} else if (strcmp(token, "notrim") == 0) {
323 				if (Eflag) {
324 					warn("Options \"notrim\" and "
325 					    "\"trimonce\" conflict");
326 					free(ops);
327 					return (NULL);
328 				}
329 				Tflag = " -T ";
330 			} else if (strcmp(token, "late") == 0) {
331 				/* ignore known option */
332 			} else if (strcmp(token, "noauto") == 0) {
333 				/* ignore known option */
334 			} else if (strcmp(token, "sw") == 0) {
335 				/* ignore known option */
336 			} else if (strcmp(token, "trimonce") == 0) {
337 				/* ignore known option */
338 			} else {
339 				warnx("Invalid option: %s", token);
340 				free(ops);
341 				return (NULL);
342 			}
343 		}
344 	} else
345 		ops = NULL;
346 
347 	/*
348 	 * If we do not have a sector size at this point, fill in
349 	 * pagesize as sector size.
350 	 */
351 	if (sectorsize_str == NULL) {
352 		/* Use pagesize as default sectorsize. */
353 		pagesize = getpagesize();
354 		pagesize_len = snprintf(NULL, 0, "%d", pagesize) + 1;
355 		p = alloca(pagesize_len);
356 		snprintf(p, pagesize_len, "%d", pagesize);
357 		sectorsize_str = p;
358 	}
359 
360 	(void)asprintf(&args, "%s%s%s%s%s%s%s%s%s -d",
361 	    aflag, aalgo, eflag, ealgo, lflag, keylen_str, Tflag,
362 	    sflag, sectorsize_str);
363 
364 	free(ops);
365 	return (args);
366 }
367 
368 static const char *
369 swap_on_off_geli(const char *name, char *mntops, int doingall)
370 {
371 	struct stat sb;
372 	char *dname, *args;
373 	int error;
374 
375 	error = stat(name, &sb);
376 
377 	if (which_prog == SWAPON) do {
378 		/* Skip if the .eli device already exists. */
379 		if (error == 0)
380 			break;
381 
382 		args = swap_on_geli_args(mntops);
383 		if (args == NULL)
384 			return (NULL);
385 
386 		dname = swap_basename(name);
387 		if (dname == NULL) {
388 			free(args);
389 			return (NULL);
390 		}
391 
392 		error = run_cmd(NULL, "%s onetime%s %s", _PATH_GELI, args,
393 		    dname);
394 
395 		free(dname);
396 		free(args);
397 
398 		if (error) {
399 			/* error occurred during creation. */
400 			if (qflag == 0)
401 				warnx("%s: Invalid parameters", name);
402 			return (NULL);
403 		}
404 	} while (0);
405 
406 	return (swap_on_off_sfile(name, doingall));
407 }
408 
409 static const char *
410 swap_on_off_md(const char *name, char *mntops, int doingall)
411 {
412 	FILE *sfd;
413 	int fd, mdunit, error;
414 	const char *ret;
415 	static char mdpath[PATH_MAX], linebuf[PATH_MAX];
416 	char *p, *vnodefile;
417 	size_t linelen;
418 	u_long ul;
419 
420 	fd = -1;
421 	sfd = NULL;
422 	if (strlen(name) == (sizeof(MD_NAME) - 1))
423 		mdunit = -1;
424 	else {
425 		errno = 0;
426 		ul = strtoul(name + 2, &p, 10);
427 		if (errno == 0) {
428 			if (*p != '\0' || ul > INT_MAX)
429 				errno = EINVAL;
430 		}
431 		if (errno) {
432 			warn("Bad device unit: %s", name);
433 			return (NULL);
434 		}
435 		mdunit = (int)ul;
436 	}
437 
438 	vnodefile = NULL;
439 	if ((p = strstr(mntops, "file=")) != NULL) {
440 		vnodefile = strdup(p + sizeof("file=") - 1);
441 		p = strchr(vnodefile, ',');
442 		if (p != NULL)
443 			*p = '\0';
444 	}
445 	if (vnodefile == NULL) {
446 		warnx("file option not found for %s", name);
447 		return (NULL);
448 	}
449 
450 	if (which_prog == SWAPON) {
451 		if (mdunit == -1) {
452 			error = run_cmd(&fd, "%s -l -n -f %s",
453 			    _PATH_MDCONFIG, vnodefile);
454 			if (error == 0) {
455 				/* md device found.  Ignore it. */
456 				close(fd);
457 				if (!qflag)
458 					warnx("%s: Device already in use",
459 					    vnodefile);
460 				free(vnodefile);
461 				return (NULL);
462 			}
463 			error = run_cmd(&fd, "%s -a -t vnode -n -f %s",
464 			    _PATH_MDCONFIG, vnodefile);
465 			if (error) {
466 				warnx("mdconfig (attach) error: file=%s",
467 				    vnodefile);
468 				free(vnodefile);
469 				return (NULL);
470 			}
471 			sfd = fdopen(fd, "r");
472 			if (sfd == NULL) {
473 				warn("mdconfig (attach) fdopen error");
474 				ret = NULL;
475 				goto err;
476 			}
477 			p = fgetln(sfd, &linelen);
478 			if (p == NULL ||
479 			    (linelen < 2 || linelen > sizeof(linebuf))) {
480 				warn("mdconfig (attach) unexpected output");
481 				ret = NULL;
482 				goto err;
483 			}
484 			strlcpy(linebuf, p, linelen);
485 			errno = 0;
486 			ul = strtoul(linebuf, &p, 10);
487 			if (errno == 0) {
488 				if (*p != '\0' || ul > INT_MAX)
489 					errno = EINVAL;
490 			}
491 			if (errno) {
492 				warn("mdconfig (attach) unexpected output: %s",
493 				    linebuf);
494 				ret = NULL;
495 				goto err;
496 			}
497 			mdunit = (int)ul;
498 		} else {
499 			error = run_cmd(&fd, "%s -l -n -f %s -u %d",
500 			    _PATH_MDCONFIG, vnodefile, mdunit);
501 			if (error == 0) {
502 				/* md device found.  Ignore it. */
503 				close(fd);
504 				if (qflag == 0)
505 					warnx("md%d on %s: Device already "
506 					    "in use", mdunit, vnodefile);
507 				free(vnodefile);
508 				return (NULL);
509 			}
510 			error = run_cmd(NULL, "%s -a -t vnode -u %d -f %s",
511 			    _PATH_MDCONFIG, mdunit, vnodefile);
512 			if (error) {
513 				warnx("mdconfig (attach) error: "
514 				    "md%d on file=%s", mdunit, vnodefile);
515 				free(vnodefile);
516 				return (NULL);
517 			}
518 		}
519 	} else /* SWAPOFF */ {
520 		if (mdunit == -1) {
521 			error = run_cmd(&fd, "%s -l -n -f %s",
522 			    _PATH_MDCONFIG, vnodefile);
523 			if (error) {
524 				/* md device not found.  Ignore it. */
525 				close(fd);
526 				if (!qflag)
527 					warnx("md on %s: Device not found",
528 					    vnodefile);
529 				free(vnodefile);
530 				return (NULL);
531 			}
532 			sfd = fdopen(fd, "r");
533 			if (sfd == NULL) {
534 				warn("mdconfig (list) fdopen error");
535 				ret = NULL;
536 				goto err;
537 			}
538 			p = fgetln(sfd, &linelen);
539 			if (p == NULL ||
540 			    (linelen < 2 || linelen > sizeof(linebuf))) {
541 				warn("mdconfig (list) unexpected output");
542 				ret = NULL;
543 				goto err;
544 			}
545 			strlcpy(linebuf, p, linelen);
546 			p = strchr(linebuf, ' ');
547 			if (p != NULL)
548 				*p = '\0';
549 			errno = 0;
550 			ul = strtoul(linebuf, &p, 10);
551 			if (errno == 0) {
552 				if (*p != '\0' || ul > INT_MAX)
553 					errno = EINVAL;
554 			}
555 			if (errno) {
556 				warn("mdconfig (list) unexpected output: %s",
557 				    linebuf);
558 				ret = NULL;
559 				goto err;
560 			}
561 			mdunit = (int)ul;
562 		} else {
563 			error = run_cmd(&fd, "%s -l -n -f %s -u %d",
564 			    _PATH_MDCONFIG, vnodefile, mdunit);
565 			if (error) {
566 				/* md device not found.  Ignore it. */
567 				close(fd);
568 				if (!qflag)
569 					warnx("md%d on %s: Device not found",
570 					    mdunit, vnodefile);
571 				free(vnodefile);
572 				return (NULL);
573 			}
574 		}
575 	}
576 	snprintf(mdpath, sizeof(mdpath), "%s%s%d", _PATH_DEV,
577 	    MD_NAME, mdunit);
578 	mdpath[sizeof(mdpath) - 1] = '\0';
579 	ret = swap_on_off_sfile(mdpath, doingall);
580 
581 	if (which_prog == SWAPOFF) {
582 		if (ret != NULL) {
583 			error = run_cmd(NULL, "%s -d -u %d",
584 			    _PATH_MDCONFIG, mdunit);
585 			if (error)
586 				warn("mdconfig (detach) detach failed: %s%s%d",
587 				    _PATH_DEV, MD_NAME, mdunit);
588 		}
589 	}
590 err:
591 	if (sfd != NULL)
592 		fclose(sfd);
593 	if (fd != -1)
594 		close(fd);
595 	free(vnodefile);
596 	return (ret);
597 }
598 
599 static int
600 run_cmd(int *ofd, const char *cmdline, ...)
601 {
602 	va_list ap;
603 	char **argv, **argvp, *cmd, *p;
604 	int argc, pid, status, rv;
605 	int pfd[2], nfd, dup2dn;
606 
607 	va_start(ap, cmdline);
608 	rv = vasprintf(&cmd, cmdline, ap);
609 	if (rv == -1) {
610 		warn("%s", __func__);
611 		va_end(ap);
612 		return (rv);
613 	}
614 	va_end(ap);
615 
616 	for (argc = 1, p = cmd; (p = strchr(p, ' ')) != NULL; p++)
617 		argc++;
618 	argv = (char **)malloc(sizeof(*argv) * (argc + 1));
619 	for (p = cmd, argvp = argv; (*argvp = strsep(&p, " ")) != NULL;)
620 		if (**argvp != '\0' && (++argvp > &argv[argc])) {
621 			*argvp = NULL;
622 			break;
623 		}
624 	/* The argv array ends up NULL-terminated here. */
625 #if 0
626 	{
627 		int i;
628 
629 		fprintf(stderr, "DEBUG: running:");
630 		/* Should be equivalent to 'cmd' (before strsep, of course). */
631 		for (i = 0; argv[i] != NULL; i++)
632 			fprintf(stderr, " %s", argv[i]);
633 		fprintf(stderr, "\n");
634 	}
635 #endif
636 	dup2dn = 1;
637 	if (ofd != NULL) {
638 		if (pipe(&pfd[0]) == -1) {
639 			warn("%s: pipe", __func__);
640 			return (-1);
641 		}
642 		*ofd = pfd[0];
643 		dup2dn = 0;
644 	}
645 	pid = fork();
646 	switch (pid) {
647 	case 0:
648 		/* Child process. */
649 		if (ofd != NULL)
650 			if (dup2(pfd[1], STDOUT_FILENO) < 0)
651 				err(1, "dup2 in %s", __func__);
652 		nfd = open(_PATH_DEVNULL, O_RDWR);
653 		if (nfd == -1)
654 			err(1, "%s: open %s", __func__, _PATH_DEVNULL);
655 		if (dup2(nfd, STDIN_FILENO) < 0)
656 			err(1, "%s: dup2", __func__);
657 		if (dup2dn && dup2(nfd, STDOUT_FILENO) < 0)
658 			err(1, "%s: dup2", __func__);
659 		if (dup2(nfd, STDERR_FILENO) < 0)
660 			err(1, "%s: dup2", __func__);
661 		execv(argv[0], argv);
662 		warn("exec: %s", argv[0]);
663 		_exit(-1);
664 	case -1:
665 		err(1, "%s: fork", __func__);
666 	}
667 	free(cmd);
668 	free(argv);
669 	while (waitpid(pid, &status, 0) != pid)
670 		;
671 	return (WEXITSTATUS(status));
672 }
673 
674 static int
675 swapon_trim(const char *name)
676 {
677 	struct stat sb;
678 	off_t ioarg[2], sz;
679 	int error, fd;
680 
681 	/* Open a descriptor to create a consumer of the device. */
682 	fd = open(name, O_WRONLY);
683 	if (fd < 0)
684 		errx(1, "Cannot open %s", name);
685 	/* Find the device size. */
686 	if (fstat(fd, &sb) < 0)
687 		errx(1, "Cannot stat %s", name);
688 	if (S_ISREG(sb.st_mode))
689 		sz = sb.st_size;
690 	else if (S_ISCHR(sb.st_mode)) {
691 		if (ioctl(fd, DIOCGMEDIASIZE, &sz) != 0)
692 			err(1, "ioctl(DIOCGMEDIASIZE)");
693 	} else
694 		errx(1, "%s has an invalid file type", name);
695 	/* Trim the device. */
696 	ioarg[0] = BBSIZE;
697 	ioarg[1] = sz - BBSIZE;
698 	if (ioctl(fd, DIOCGDELETE, ioarg) != 0)
699 		warn("ioctl(DIOCGDELETE)");
700 
701 	/* Start using the device for swapping, creating a second consumer. */
702 	error = swapon(name);
703 
704 	/*
705 	 * Do not close the device until the swap pager has attempted to create
706 	 * another consumer.  For GELI devices created with the 'detach -l'
707 	 * option, removing the last consumer causes the device to be detached
708 	 * - that is, to disappear.  This ordering ensures that the device will
709 	 * not be detached until swapoff is called.
710 	 */
711 	close(fd);
712 	return (error);
713 }
714 
715 static const char *
716 swap_on_off_sfile(const char *name, int doingall)
717 {
718 	int error;
719 
720 	if (which_prog == SWAPON)
721 		error = Eflag ? swapon_trim(name) : swapon(name);
722 	else /* SWAPOFF */
723 		error = swapoff(name, fflag ? SWAPOFF_FORCE : 0);
724 
725 	if (error == -1) {
726 		switch (errno) {
727 		case EBUSY:
728 			if (doingall == 0)
729 				warnx("%s: Device already in use", name);
730 			break;
731 		case EINVAL:
732 			if (which_prog == SWAPON)
733 				warnx("%s: NSWAPDEV limit reached", name);
734 			else if (doingall == 0)
735 				warn("%s", name);
736 			break;
737 		default:
738 			warn("%s", name);
739 			break;
740 		}
741 		return (NULL);
742 	}
743 	return (name);
744 }
745 
746 static void
747 usage(void)
748 {
749 
750 	fprintf(stderr, "usage: %s ", getprogname());
751 	switch(orig_prog) {
752 	case SWAPON:
753 	    fprintf(stderr, "[-F fstab] -aLq | [-E] file ...\n");
754 	    break;
755 	case SWAPOFF:
756 	    fprintf(stderr, "[-F fstab] -afLq | file ...\n");
757 	    break;
758 	case SWAPCTL:
759 	    fprintf(stderr, "[-AghklmsU] [-a file ... | -d file ...]\n");
760 	    break;
761 	}
762 	exit(1);
763 }
764 
765 static void
766 sizetobuf(char *buf, size_t bufsize, int hflag, long long val, int hlen,
767     long blocksize)
768 {
769 	char tmp[16];
770 
771 	if (hflag == 'H') {
772 		humanize_number(tmp, 5, (int64_t)val, "", HN_AUTOSCALE,
773 		    HN_B | HN_NOSPACE | HN_DECIMAL);
774 		snprintf(buf, bufsize, "%*s", hlen, tmp);
775 	} else
776 		snprintf(buf, bufsize, "%*lld", hlen, val / blocksize);
777 }
778 
779 static void
780 swaplist(int lflag, int sflag, int hflag)
781 {
782 	size_t mibsize, size;
783 	struct xswdev xsw;
784 	int hlen, mib[16], n, pagesize;
785 	long blocksize;
786 	long long total = 0;
787 	long long used = 0;
788 	long long tmp_total;
789 	long long tmp_used;
790 	char buf[32];
791 
792 	pagesize = getpagesize();
793 	switch(hflag) {
794 	case 'G':
795 		blocksize = 1024 * 1024 * 1024;
796 		strlcpy(buf, "1GB-blocks", sizeof(buf));
797 		hlen = 10;
798 		break;
799 	case 'H':
800 		blocksize = -1;
801 		strlcpy(buf, "Bytes", sizeof(buf));
802 		hlen = 10;
803 		break;
804 	case 'K':
805 		blocksize = 1024;
806 		strlcpy(buf, "1kB-blocks", sizeof(buf));
807 		hlen = 10;
808 		break;
809 	case 'M':
810 		blocksize = 1024 * 1024;
811 		strlcpy(buf, "1MB-blocks", sizeof(buf));
812 		hlen = 10;
813 		break;
814 	default:
815 		getbsize(&hlen, &blocksize);
816 		snprintf(buf, sizeof(buf), "%ld-blocks", blocksize);
817 		break;
818 	}
819 
820 	mibsize = nitems(mib);
821 	if (sysctlnametomib("vm.swap_info", mib, &mibsize) == -1)
822 		err(1, "sysctlnametomib()");
823 
824 	if (lflag) {
825 		printf("%-13s %*s %*s\n",
826 		    "Device:",
827 		    hlen, buf,
828 		    hlen, "Used:");
829 	}
830 
831 	for (n = 0; ; ++n) {
832 		mib[mibsize] = n;
833 		size = sizeof xsw;
834 		if (sysctl(mib, mibsize + 1, &xsw, &size, NULL, 0) == -1)
835 			break;
836 		if (xsw.xsw_version != XSWDEV_VERSION)
837 			errx(1, "xswdev version mismatch");
838 
839 		tmp_total = (long long)xsw.xsw_nblks * pagesize;
840 		tmp_used  = (long long)xsw.xsw_used * pagesize;
841 		total += tmp_total;
842 		used  += tmp_used;
843 		if (lflag) {
844 			sizetobuf(buf, sizeof(buf), hflag, tmp_total, hlen,
845 			    blocksize);
846 			printf("/dev/%-8s %s ", devname(xsw.xsw_dev, S_IFCHR),
847 			    buf);
848 			sizetobuf(buf, sizeof(buf), hflag, tmp_used, hlen,
849 			    blocksize);
850 			printf("%s\n", buf);
851 		}
852 	}
853 	if (errno != ENOENT)
854 		err(1, "sysctl()");
855 
856 	if (sflag) {
857 		sizetobuf(buf, sizeof(buf), hflag, total, hlen, blocksize);
858 		printf("Total:        %s ", buf);
859 		sizetobuf(buf, sizeof(buf), hflag, used, hlen, blocksize);
860 		printf("%s\n", buf);
861 	}
862 }
863 
864