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