diff --git a/sbin/swapon/swapon.8 b/sbin/swapon/swapon.8 index 7ae28031dc1e..0146a7750e12 100644 --- a/sbin/swapon/swapon.8 +++ b/sbin/swapon/swapon.8 @@ -1,237 +1,243 @@ .\" Copyright (c) 1980, 1991, 1993 .\" The Regents of the University of California. All rights reserved. .\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions .\" are met: .\" 1. Redistributions of source code must retain the above copyright .\" notice, this list of conditions and the following disclaimer. .\" 2. Redistributions in binary form must reproduce the above copyright .\" notice, this list of conditions and the following disclaimer in the .\" documentation and/or other materials provided with the distribution. .\" 3. Neither the name of the University nor the names of its contributors .\" may be used to endorse or promote products derived from this software .\" without specific prior written permission. .\" .\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND .\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE .\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE .\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE .\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL .\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS .\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) .\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT .\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" .\" @(#)swapon.8 8.1 (Berkeley) 6/5/93 .\" $FreeBSD$ .\" -.Dd May 19, 2020 +.Dd November 29, 2021 .Dt SWAPON 8 .Os .Sh NAME .Nm swapon , swapoff , swapctl .Nd "specify devices for paging and swapping" .Sh SYNOPSIS .Nm swapon .Oo Fl F Ar fstab .Oc .Fl aLq | E Ar .Nm swapoff .Oo Fl F Ar fstab .Oc -.Fl aLq | Ar +.Fl afLq | Ar .Nm swapctl -.Op Fl AghklmsU +.Op Fl AfghklmsU .Oo .Fl a Ar | .Fl d Ar .Oc .Sh DESCRIPTION The .Nm swapon , swapoff and .Nm swapctl utilities are used to control swap devices in the system. At boot time all swap entries in .Pa /etc/fstab are added automatically when the system goes multi-user. Swap devices use a fixed interleave; the maximum number of devices is unlimited. There is no priority mechanism. .Pp The .Nm swapon utility adds the specified swap devices to the system. If the .Fl a option is used, all swap devices in .Pa /etc/fstab will be added, unless their .Dq noauto or .Dq late option is also set. If the .Fl L option is specified, swap devices with the .Dq late option will be added as well as ones with no option. If the .Fl q option is used, informational messages will not be written to standard output when a swap device is added. The .Fl E option causes each of following devices to receive a .Dv BIO_DELETE command. This command marks the device's blocks as unused, except those that might store a disk label. This marking can erase a crash dump. To delay .Nm swapon for a device until after .Nm savecore has copied the crash dump to another location, use the .Dq late option. .Pp The .Nm swapoff utility removes the specified swap devices from the system. If the .Fl a option is used, all swap devices in .Pa /etc/fstab will be removed, unless their .Dq noauto option is also set. If the .Fl L option is specified, only swap devices with the .Dq late option will be removed. If the .Fl q option is used, informational messages will not be written to standard output when a swap device is removed. Note that .Nm swapoff -will fail and refuse to remove a swap device if there is insufficient -VM (memory + remaining swap devices) to run the system. +will fail and refuse to remove a swap device if a very conservative +check does not conclude that there is sufficient VM (memory + +remaining swap devices) to run the system. +The +.Fl f +option turns off this check, which could deadlock the system +if there is insufficient swap space remaining. +.Pp The .Nm swapoff utility must move swapped pages out of the device being removed which could lead to high system loads for a period of time, depending on how much data has been swapped out to that device. .Pp Other options supported by both .Nm swapon and .Nm swapoff are as follows: .Bl -tag -width indent .It Fl F Ar fstab Specify the .Pa fstab file to use (in conjunction with .Fl a Ns ). .El .Pp The .Nm swapctl utility exists primarily for those familiar with other .Bx Ns s and may be used to add, remove, or list swap devices. Note that the .Fl a option is used differently in .Nm swapctl and indicates that a specific list of devices should be added. The .Fl d option indicates that a specific list should be removed. The .Fl A and .Fl U options to .Nm swapctl operate on all swap entries in .Pa /etc/fstab which do not have their .Dq noauto option set. .Pp Swap information can be generated using the .Xr swapinfo 8 utility, .Nm pstat .Fl s , or .Nm swapctl .Fl l . The .Nm swapctl utility has the following options for listing swap: .Bl -tag -width indent .It Fl h Output values in human-readable form. .It Fl g Output values in gigabytes. .It Fl k Output values in kilobytes. .It Fl m Output values in megabytes. .It Fl l List the devices making up system swap. .It Fl s Print a summary line for system swap. .Pp The .Ev BLOCKSIZE environment variable is used if not specifically overridden. 512 byte blocks are used by default. .El .Sh FILES .Bl -tag -width ".Pa /dev/{ada,da}?s?b" -compact .It Pa /dev/{ada,da}?s?b standard paging devices .It Pa /dev/md? memory disk devices .It Pa /etc/fstab .Tn ASCII file system description table .El .Sh DIAGNOSTICS These utilities may fail for the reasons described in .Xr swapon 2 . .Sh SEE ALSO .Xr swapon 2 , .Xr fstab 5 , .Xr init 8 , .Xr mdconfig 8 , .Xr pstat 8 , .Xr rc 8 .Sh HISTORY The .Nm swapon utility appeared in .Bx 4.0 . The .Nm swapoff and .Nm swapctl utilities appeared in .Fx 5.1 . diff --git a/sbin/swapon/swapon.c b/sbin/swapon/swapon.c index 5adc870adf59..d644d99e7913 100644 --- a/sbin/swapon/swapon.c +++ b/sbin/swapon/swapon.c @@ -1,930 +1,943 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1980, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #if 0 #ifndef lint static const char copyright[] = "@(#) Copyright (c) 1980, 1993\n\ The Regents of the University of California. All rights reserved.\n"; #endif /* not lint */ #ifndef lint static char sccsid[] = "@(#)swapon.c 8.1 (Berkeley) 6/5/93"; #endif /* not lint */ #endif #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include +#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static void usage(void); static const char *swap_on_off(const char *, int, char *); static const char *swap_on_off_gbde(const char *, int); static const char *swap_on_off_geli(const char *, char *, int); static const char *swap_on_off_md(const char *, char *, int); static const char *swap_on_off_sfile(const char *, int); static void swaplist(int, int, int); static int run_cmd(int *, const char *, ...) __printflike(2, 3); static enum { SWAPON, SWAPOFF, SWAPCTL } orig_prog, which_prog = SWAPCTL; -static int Eflag, qflag; +static int Eflag, fflag, qflag; int main(int argc, char **argv) { struct fstab *fsp; const char *swfile; char *ptr; int ret, ch, doall; int sflag, lflag, late, hflag; const char *etc_fstab; sflag = lflag = late = hflag = 0; if ((ptr = strrchr(argv[0], '/')) == NULL) ptr = argv[0]; if (strstr(ptr, "swapon") != NULL) which_prog = SWAPON; else if (strstr(ptr, "swapoff") != NULL) which_prog = SWAPOFF; orig_prog = which_prog; doall = 0; etc_fstab = NULL; - while ((ch = getopt(argc, argv, "AadEghklLmqsUF:")) != -1) { + while ((ch = getopt(argc, argv, "AadEfghklLmqsUF:")) != -1) { switch(ch) { case 'A': if (which_prog == SWAPCTL) { doall = 1; which_prog = SWAPON; } else usage(); break; case 'a': if (which_prog == SWAPON || which_prog == SWAPOFF) doall = 1; else which_prog = SWAPON; break; case 'd': if (which_prog == SWAPCTL) which_prog = SWAPOFF; else usage(); break; case 'E': if (which_prog == SWAPON) Eflag = 2; else usage(); break; + case 'f': + if (which_prog == SWAPOFF) + fflag = 1; + else + usage(); + break; case 'g': hflag = 'G'; break; case 'h': hflag = 'H'; break; case 'k': hflag = 'K'; break; case 'l': lflag = 1; break; case 'L': late = 1; break; case 'm': hflag = 'M'; break; case 'q': if (which_prog == SWAPON || which_prog == SWAPOFF) qflag = 1; break; case 's': sflag = 1; break; case 'U': if (which_prog == SWAPCTL) { doall = 1; which_prog = SWAPOFF; } else usage(); break; case 'F': etc_fstab = optarg; break; case '?': default: usage(); } } argv += optind; ret = 0; swfile = NULL; if (etc_fstab != NULL) setfstab(etc_fstab); if (which_prog == SWAPON || which_prog == SWAPOFF) { if (doall) { while ((fsp = getfsent()) != NULL) { if (strcmp(fsp->fs_type, FSTAB_SW) != 0) continue; if (strstr(fsp->fs_mntops, "noauto") != NULL) continue; if (which_prog != SWAPOFF && strstr(fsp->fs_mntops, "late") && late == 0) continue; if (which_prog == SWAPOFF && strstr(fsp->fs_mntops, "late") == NULL && late != 0) continue; Eflag |= (strstr(fsp->fs_mntops, "trimonce") != NULL); swfile = swap_on_off(fsp->fs_spec, 1, fsp->fs_mntops); Eflag &= ~1; if (swfile == NULL) { ret = 1; continue; } if (qflag == 0) { printf("%s: %sing %s as swap device\n", getprogname(), (which_prog == SWAPOFF) ? "remov" : "add", swfile); } } } else if (*argv == NULL) usage(); for (; *argv; ++argv) { swfile = swap_on_off(*argv, 0, NULL); if (swfile == NULL) { ret = 1; continue; } if (orig_prog == SWAPCTL) { printf("%s: %sing %s as swap device\n", getprogname(), (which_prog == SWAPOFF) ? "remov" : "add", swfile); } } } else { if (lflag || sflag) swaplist(lflag, sflag, hflag); else usage(); } exit(ret); } static const char * swap_on_off(const char *name, int doingall, char *mntops) { char *base, *basebuf; /* Swap on vnode-backed md(4) device. */ if (mntops != NULL && (fnmatch(_PATH_DEV MD_NAME "[0-9]*", name, 0) == 0 || fnmatch(MD_NAME "[0-9]*", name, 0) == 0 || strncmp(_PATH_DEV MD_NAME, name, sizeof(_PATH_DEV) + sizeof(MD_NAME)) == 0 || strncmp(MD_NAME, name, sizeof(MD_NAME)) == 0)) return (swap_on_off_md(name, mntops, doingall)); basebuf = strdup(name); base = basename(basebuf); /* Swap on encrypted device by GEOM_BDE. */ if (fnmatch("*.bde", base, 0) == 0) { free(basebuf); return (swap_on_off_gbde(name, doingall)); } /* Swap on encrypted device by GEOM_ELI. */ if (fnmatch("*.eli", base, 0) == 0) { free(basebuf); return (swap_on_off_geli(name, mntops, doingall)); } /* Swap on special file. */ free(basebuf); return (swap_on_off_sfile(name, doingall)); } /* Strip off .bde or .eli suffix from swap device name */ static char * swap_basename(const char *name) { char *dname, *p; dname = strdup(name); p = strrchr(dname, '.'); /* assert(p != NULL); */ *p = '\0'; return (dname); } static const char * swap_on_off_gbde(const char *name, int doingall) { const char *ret; char pass[64 * 2 + 1]; unsigned char bpass[64]; char *dname; int i, error; dname = swap_basename(name); if (dname == NULL) return (NULL); if (which_prog == SWAPON) { arc4random_buf(bpass, sizeof(bpass)); for (i = 0; i < (int)sizeof(bpass); i++) sprintf(&pass[2 * i], "%02x", bpass[i]); pass[sizeof(pass) - 1] = '\0'; error = run_cmd(NULL, "%s init %s -P %s", _PATH_GBDE, dname, pass); if (error) { /* bde device found. Ignore it. */ free(dname); if (qflag == 0) warnx("%s: Device already in use", name); return (NULL); } error = run_cmd(NULL, "%s attach %s -p %s", _PATH_GBDE, dname, pass); free(dname); if (error) { warnx("gbde (attach) error: %s", name); return (NULL); } } ret = swap_on_off_sfile(name, doingall); if (which_prog == SWAPOFF) { error = run_cmd(NULL, "%s detach %s", _PATH_GBDE, dname); free(dname); if (error) { /* bde device not found. Ignore it. */ if (qflag == 0) warnx("%s: Device not found", name); return (NULL); } } return (ret); } /* Build geli(8) arguments from mntops */ static char * swap_on_geli_args(const char *mntops) { const char *aalgo, *ealgo, *keylen_str, *sectorsize_str; const char *aflag, *eflag, *lflag, *Tflag, *sflag; char *p, *args, *token, *string, *ops; int pagesize; size_t pagesize_len; u_long ul; /* Use built-in defaults for geli(8). */ aalgo = ealgo = keylen_str = ""; aflag = eflag = lflag = Tflag = ""; /* We will always specify sectorsize. */ sflag = " -s "; sectorsize_str = NULL; if (mntops != NULL) { string = ops = strdup(mntops); while ((token = strsep(&string, ",")) != NULL) { if ((p = strstr(token, "aalgo=")) == token) { aalgo = p + sizeof("aalgo=") - 1; aflag = " -a "; } else if ((p = strstr(token, "ealgo=")) == token) { ealgo = p + sizeof("ealgo=") - 1; eflag = " -e "; } else if ((p = strstr(token, "keylen=")) == token) { keylen_str = p + sizeof("keylen=") - 1; errno = 0; ul = strtoul(keylen_str, &p, 10); if (errno == 0) { if (*p != '\0' || ul > INT_MAX) errno = EINVAL; } if (errno) { warn("Invalid keylen: %s", keylen_str); free(ops); return (NULL); } lflag = " -l "; } else if ((p = strstr(token, "sectorsize=")) == token) { sectorsize_str = p + sizeof("sectorsize=") - 1; errno = 0; ul = strtoul(sectorsize_str, &p, 10); if (errno == 0) { if (*p != '\0' || ul > INT_MAX) errno = EINVAL; } if (errno) { warn("Invalid sectorsize: %s", sectorsize_str); free(ops); return (NULL); } } else if (strcmp(token, "notrim") == 0) { if (Eflag) { warn("Options \"notrim\" and " "\"trimonce\" conflict"); free(ops); return (NULL); } Tflag = " -T "; } else if (strcmp(token, "late") == 0) { /* ignore known option */ } else if (strcmp(token, "noauto") == 0) { /* ignore known option */ } else if (strcmp(token, "sw") == 0) { /* ignore known option */ } else if (strcmp(token, "trimonce") == 0) { /* ignore known option */ } else { warnx("Invalid option: %s", token); free(ops); return (NULL); } } } else ops = NULL; /* * If we do not have a sector size at this point, fill in * pagesize as sector size. */ if (sectorsize_str == NULL) { /* Use pagesize as default sectorsize. */ pagesize = getpagesize(); pagesize_len = snprintf(NULL, 0, "%d", pagesize) + 1; p = alloca(pagesize_len); snprintf(p, pagesize_len, "%d", pagesize); sectorsize_str = p; } (void)asprintf(&args, "%s%s%s%s%s%s%s%s%s -d", aflag, aalgo, eflag, ealgo, lflag, keylen_str, Tflag, sflag, sectorsize_str); free(ops); return (args); } static const char * swap_on_off_geli(const char *name, char *mntops, int doingall) { struct stat sb; char *dname, *args; int error; error = stat(name, &sb); if (which_prog == SWAPON) do { /* Skip if the .eli device already exists. */ if (error == 0) break; args = swap_on_geli_args(mntops); if (args == NULL) return (NULL); dname = swap_basename(name); if (dname == NULL) { free(args); return (NULL); } error = run_cmd(NULL, "%s onetime%s %s", _PATH_GELI, args, dname); free(dname); free(args); if (error) { /* error occurred during creation. */ if (qflag == 0) warnx("%s: Invalid parameters", name); return (NULL); } } while (0); return (swap_on_off_sfile(name, doingall)); } static const char * swap_on_off_md(const char *name, char *mntops, int doingall) { FILE *sfd; int fd, mdunit, error; const char *ret; static char mdpath[PATH_MAX], linebuf[PATH_MAX]; char *p, *vnodefile; size_t linelen; u_long ul; fd = -1; sfd = NULL; if (strlen(name) == (sizeof(MD_NAME) - 1)) mdunit = -1; else { errno = 0; ul = strtoul(name + 2, &p, 10); if (errno == 0) { if (*p != '\0' || ul > INT_MAX) errno = EINVAL; } if (errno) { warn("Bad device unit: %s", name); return (NULL); } mdunit = (int)ul; } vnodefile = NULL; if ((p = strstr(mntops, "file=")) != NULL) { vnodefile = strdup(p + sizeof("file=") - 1); p = strchr(vnodefile, ','); if (p != NULL) *p = '\0'; } if (vnodefile == NULL) { warnx("file option not found for %s", name); return (NULL); } if (which_prog == SWAPON) { if (mdunit == -1) { error = run_cmd(&fd, "%s -l -n -f %s", _PATH_MDCONFIG, vnodefile); if (error == 0) { /* md device found. Ignore it. */ close(fd); if (!qflag) warnx("%s: Device already in use", vnodefile); free(vnodefile); return (NULL); } error = run_cmd(&fd, "%s -a -t vnode -n -f %s", _PATH_MDCONFIG, vnodefile); if (error) { warnx("mdconfig (attach) error: file=%s", vnodefile); free(vnodefile); return (NULL); } sfd = fdopen(fd, "r"); if (sfd == NULL) { warn("mdconfig (attach) fdopen error"); ret = NULL; goto err; } p = fgetln(sfd, &linelen); if (p == NULL || (linelen < 2 || linelen > sizeof(linebuf))) { warn("mdconfig (attach) unexpected output"); ret = NULL; goto err; } strlcpy(linebuf, p, linelen); errno = 0; ul = strtoul(linebuf, &p, 10); if (errno == 0) { if (*p != '\0' || ul > INT_MAX) errno = EINVAL; } if (errno) { warn("mdconfig (attach) unexpected output: %s", linebuf); ret = NULL; goto err; } mdunit = (int)ul; } else { error = run_cmd(&fd, "%s -l -n -f %s -u %d", _PATH_MDCONFIG, vnodefile, mdunit); if (error == 0) { /* md device found. Ignore it. */ close(fd); if (qflag == 0) warnx("md%d on %s: Device already " "in use", mdunit, vnodefile); free(vnodefile); return (NULL); } error = run_cmd(NULL, "%s -a -t vnode -u %d -f %s", _PATH_MDCONFIG, mdunit, vnodefile); if (error) { warnx("mdconfig (attach) error: " "md%d on file=%s", mdunit, vnodefile); free(vnodefile); return (NULL); } } } else /* SWAPOFF */ { if (mdunit == -1) { error = run_cmd(&fd, "%s -l -n -f %s", _PATH_MDCONFIG, vnodefile); if (error) { /* md device not found. Ignore it. */ close(fd); if (!qflag) warnx("md on %s: Device not found", vnodefile); free(vnodefile); return (NULL); } sfd = fdopen(fd, "r"); if (sfd == NULL) { warn("mdconfig (list) fdopen error"); ret = NULL; goto err; } p = fgetln(sfd, &linelen); if (p == NULL || (linelen < 2 || linelen > sizeof(linebuf))) { warn("mdconfig (list) unexpected output"); ret = NULL; goto err; } strlcpy(linebuf, p, linelen); p = strchr(linebuf, ' '); if (p != NULL) *p = '\0'; errno = 0; ul = strtoul(linebuf, &p, 10); if (errno == 0) { if (*p != '\0' || ul > INT_MAX) errno = EINVAL; } if (errno) { warn("mdconfig (list) unexpected output: %s", linebuf); ret = NULL; goto err; } mdunit = (int)ul; } else { error = run_cmd(&fd, "%s -l -n -f %s -u %d", _PATH_MDCONFIG, vnodefile, mdunit); if (error) { /* md device not found. Ignore it. */ close(fd); if (!qflag) warnx("md%d on %s: Device not found", mdunit, vnodefile); free(vnodefile); return (NULL); } } } snprintf(mdpath, sizeof(mdpath), "%s%s%d", _PATH_DEV, MD_NAME, mdunit); mdpath[sizeof(mdpath) - 1] = '\0'; ret = swap_on_off_sfile(mdpath, doingall); if (which_prog == SWAPOFF) { if (ret != NULL) { error = run_cmd(NULL, "%s -d -u %d", _PATH_MDCONFIG, mdunit); if (error) warn("mdconfig (detach) detach failed: %s%s%d", _PATH_DEV, MD_NAME, mdunit); } } err: if (sfd != NULL) fclose(sfd); if (fd != -1) close(fd); free(vnodefile); return (ret); } static int run_cmd(int *ofd, const char *cmdline, ...) { va_list ap; char **argv, **argvp, *cmd, *p; int argc, pid, status, rv; int pfd[2], nfd, dup2dn; va_start(ap, cmdline); rv = vasprintf(&cmd, cmdline, ap); if (rv == -1) { warn("%s", __func__); va_end(ap); return (rv); } va_end(ap); for (argc = 1, p = cmd; (p = strchr(p, ' ')) != NULL; p++) argc++; argv = (char **)malloc(sizeof(*argv) * (argc + 1)); for (p = cmd, argvp = argv; (*argvp = strsep(&p, " ")) != NULL;) if (**argvp != '\0' && (++argvp > &argv[argc])) { *argvp = NULL; break; } /* The argv array ends up NULL-terminated here. */ #if 0 { int i; fprintf(stderr, "DEBUG: running:"); /* Should be equivalent to 'cmd' (before strsep, of course). */ for (i = 0; argv[i] != NULL; i++) fprintf(stderr, " %s", argv[i]); fprintf(stderr, "\n"); } #endif dup2dn = 1; if (ofd != NULL) { if (pipe(&pfd[0]) == -1) { warn("%s: pipe", __func__); return (-1); } *ofd = pfd[0]; dup2dn = 0; } pid = fork(); switch (pid) { case 0: /* Child process. */ if (ofd != NULL) if (dup2(pfd[1], STDOUT_FILENO) < 0) err(1, "dup2 in %s", __func__); nfd = open(_PATH_DEVNULL, O_RDWR); if (nfd == -1) err(1, "%s: open %s", __func__, _PATH_DEVNULL); if (dup2(nfd, STDIN_FILENO) < 0) err(1, "%s: dup2", __func__); if (dup2dn && dup2(nfd, STDOUT_FILENO) < 0) err(1, "%s: dup2", __func__); if (dup2(nfd, STDERR_FILENO) < 0) err(1, "%s: dup2", __func__); execv(argv[0], argv); warn("exec: %s", argv[0]); _exit(-1); case -1: err(1, "%s: fork", __func__); } free(cmd); free(argv); while (waitpid(pid, &status, 0) != pid) ; return (WEXITSTATUS(status)); } static int swapon_trim(const char *name) { struct stat sb; off_t ioarg[2], sz; int error, fd; /* Open a descriptor to create a consumer of the device. */ fd = open(name, O_WRONLY); if (fd < 0) errx(1, "Cannot open %s", name); /* Find the device size. */ if (fstat(fd, &sb) < 0) errx(1, "Cannot stat %s", name); if (S_ISREG(sb.st_mode)) sz = sb.st_size; else if (S_ISCHR(sb.st_mode)) { if (ioctl(fd, DIOCGMEDIASIZE, &sz) != 0) err(1, "ioctl(DIOCGMEDIASIZE)"); } else errx(1, "%s has an invalid file type", name); /* Trim the device. */ ioarg[0] = BBSIZE; ioarg[1] = sz - BBSIZE; if (ioctl(fd, DIOCGDELETE, ioarg) != 0) warn("ioctl(DIOCGDELETE)"); /* Start using the device for swapping, creating a second consumer. */ error = swapon(name); /* * Do not close the device until the swap pager has attempted to create * another consumer. For GELI devices created with the 'detach -l' * option, removing the last consumer causes the device to be detached * - that is, to disappear. This ordering ensures that the device will * not be detached until swapoff is called. */ close(fd); return (error); } static const char * swap_on_off_sfile(const char *name, int doingall) { + struct swapoff_new_args sa; int error; if (which_prog == SWAPON) error = Eflag ? swapon_trim(name) : swapon(name); - else /* SWAPOFF */ - error = swapoff(name); + else { /* SWAPOFF */ + bzero(&sa, sizeof(sa)); + sa.name = name; + if (fflag) + sa.flags |= SWAPOFF_FORCE; + error = swapoff((const char *)&sa); + } if (error == -1) { switch (errno) { case EBUSY: if (doingall == 0) warnx("%s: Device already in use", name); break; case EINVAL: if (which_prog == SWAPON) warnx("%s: NSWAPDEV limit reached", name); else if (doingall == 0) warn("%s", name); break; default: warn("%s", name); break; } return (NULL); } return (name); } static void usage(void) { fprintf(stderr, "usage: %s ", getprogname()); switch(orig_prog) { case SWAPON: fprintf(stderr, "[-F fstab] -aLq | [-E] file ...\n"); break; case SWAPOFF: - fprintf(stderr, "[-F fstab] -aLq | file ...\n"); + fprintf(stderr, "[-F fstab] -afLq | file ...\n"); break; case SWAPCTL: fprintf(stderr, "[-AghklmsU] [-a file ... | -d file ...]\n"); break; } exit(1); } static void sizetobuf(char *buf, size_t bufsize, int hflag, long long val, int hlen, long blocksize) { char tmp[16]; if (hflag == 'H') { humanize_number(tmp, 5, (int64_t)val, "", HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL); snprintf(buf, bufsize, "%*s", hlen, tmp); } else snprintf(buf, bufsize, "%*lld", hlen, val / blocksize); } static void swaplist(int lflag, int sflag, int hflag) { size_t mibsize, size; struct xswdev xsw; int hlen, mib[16], n, pagesize; long blocksize; long long total = 0; long long used = 0; long long tmp_total; long long tmp_used; char buf[32]; pagesize = getpagesize(); switch(hflag) { case 'G': blocksize = 1024 * 1024 * 1024; strlcpy(buf, "1GB-blocks", sizeof(buf)); hlen = 10; break; case 'H': blocksize = -1; strlcpy(buf, "Bytes", sizeof(buf)); hlen = 10; break; case 'K': blocksize = 1024; strlcpy(buf, "1kB-blocks", sizeof(buf)); hlen = 10; break; case 'M': blocksize = 1024 * 1024; strlcpy(buf, "1MB-blocks", sizeof(buf)); hlen = 10; break; default: getbsize(&hlen, &blocksize); snprintf(buf, sizeof(buf), "%ld-blocks", blocksize); break; } mibsize = nitems(mib); if (sysctlnametomib("vm.swap_info", mib, &mibsize) == -1) err(1, "sysctlnametomib()"); if (lflag) { printf("%-13s %*s %*s\n", "Device:", hlen, buf, hlen, "Used:"); } for (n = 0; ; ++n) { mib[mibsize] = n; size = sizeof xsw; if (sysctl(mib, mibsize + 1, &xsw, &size, NULL, 0) == -1) break; if (xsw.xsw_version != XSWDEV_VERSION) errx(1, "xswdev version mismatch"); tmp_total = (long long)xsw.xsw_nblks * pagesize; tmp_used = (long long)xsw.xsw_used * pagesize; total += tmp_total; used += tmp_used; if (lflag) { sizetobuf(buf, sizeof(buf), hflag, tmp_total, hlen, blocksize); printf("/dev/%-8s %s ", devname(xsw.xsw_dev, S_IFCHR), buf); sizetobuf(buf, sizeof(buf), hflag, tmp_used, hlen, blocksize); printf("%s\n", buf); } } if (errno != ENOENT) err(1, "sysctl()"); if (sflag) { sizetobuf(buf, sizeof(buf), hflag, total, hlen, blocksize); printf("Total: %s ", buf); sizetobuf(buf, sizeof(buf), hflag, used, hlen, blocksize); printf("%s\n", buf); } }