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