504f5e294e
expression howmany(BBSIZE, PAGE_SIZE), where BBSIZE is the size of the boot block area. That can be less than 2 if PAGE_SIZE is big. swapon(8) has an option to trim (delete) all the blocks of a device at startup. However, if the first of those blocks is a bsd label, then trimming those blocks is destructive. Change swapon to leave the first BBSIZE bytes untrimmed. Update manual pages to reflect changes in how swapon and how it may be used, espeically in association with savecore. Reviewed by: alc Approved by: markj (mentor) MFC after: 3 days Differential Revision: https://reviews.freebsd.org/D21191
933 lines
22 KiB
C
933 lines
22 KiB
C
/*-
|
|
* 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 <sys/cdefs.h>
|
|
__FBSDID("$FreeBSD$");
|
|
|
|
#include <sys/param.h>
|
|
#include <sys/disk.h>
|
|
#include <sys/disklabel.h>
|
|
#include <sys/mdioctl.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/sysctl.h>
|
|
#include <sys/wait.h>
|
|
#include <vm/vm_param.h>
|
|
|
|
#include <err.h>
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <fnmatch.h>
|
|
#include <fstab.h>
|
|
#include <libgen.h>
|
|
#include <libutil.h>
|
|
#include <limits.h>
|
|
#include <paths.h>
|
|
#include <stdarg.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
|
|
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;
|
|
|
|
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) {
|
|
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 '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;
|
|
}
|
|
strncpy(linebuf, p, linelen);
|
|
linebuf[linelen - 1] = '\0';
|
|
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) - 1)) {
|
|
warn("mdconfig (list) unexpected output");
|
|
ret = NULL;
|
|
goto err;
|
|
}
|
|
strncpy(linebuf, p, linelen);
|
|
linebuf[linelen - 1] = '\0';
|
|
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)
|
|
{
|
|
int error;
|
|
|
|
if (which_prog == SWAPON)
|
|
error = Eflag ? swapon_trim(name) : swapon(name);
|
|
else /* SWAPOFF */
|
|
error = swapoff(name);
|
|
|
|
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");
|
|
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);
|
|
}
|
|
}
|
|
|