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