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
57static void usage(void) __dead2;
58static const char *swap_on_off(const char *, int, char *);
59static const char *swap_on_off_geli(const char *, char *, int);
60static const char *swap_on_off_md(const char *, char *, int);
61static const char *swap_on_off_sfile(const char *, int);
62static void swaplist(int, int, int);
63static int run_cmd(int *, const char *, ...) __printflike(2, 3);
64
65static enum { SWAPON, SWAPOFF, SWAPCTL } orig_prog, which_prog = SWAPCTL;
66
67static int Eflag, fflag, qflag;
68
69int
70main(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
223static const char *
224swap_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 */
252static char *
253swap_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 */
266static char *
267swap_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
368static const char *
369swap_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
409static const char *
410swap_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	}
590err:
591	if (sfd != NULL)
592		fclose(sfd);
593	if (fd != -1)
594		close(fd);
595	free(vnodefile);
596	return (ret);
597}
598
599static int
600run_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
674static int
675swapon_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
715static const char *
716swap_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
746static void
747usage(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
765static void
766sizetobuf(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
779static void
780swaplist(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