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