root/usr.sbin/vnconfig/vnconfig.c
/*      $NetBSD: vnconfig.c,v 1.48 2018/10/07 20:30:50 wiz Exp $        */

/*-
 * Copyright (c) 1997 The NetBSD Foundation, Inc.
 * All rights reserved.
 *
 * This code is derived from software contributed to The NetBSD Foundation
 * by Jason R. Thorpe.
 *
 * 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.
 *
 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
 */

/*
 * Copyright (c) 1993 University of Utah.
 * Copyright (c) 1990, 1993
 *      The Regents of the University of California.  All rights reserved.
 *
 * This code is derived from software contributed to Berkeley by
 * the Systems Programming Group of the University of Utah Computer
 * Science Department.
 *
 * 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.
 *
 * from: Utah $Hdr: vnconfig.c 1.1 93/12/15$
 *
 *      @(#)vnconfig.c  8.1 (Berkeley) 12/15/93
 */

#include <sys/param.h>
#include <sys/ioctl.h>
#include <sys/mount.h>
#include <sys/buf.h>
#include <sys/disklabel.h>
#include <sys/disk.h>

#include <dev/vndvar.h>

#include <disktab.h>
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <util.h>
#include <paths.h>
#include <limits.h>

#define VND_CONFIG      1
#define VND_UNCONFIG    2
#define VND_GET         3

/* with -l we always print at least this many entries */
#define DUMMY_FREE      4

static int      verbose = 0;
static int      readonly = 0;
static int      fileio = 0;
static int      force = 0;
static int      compressed = 0;
static int      minimum = DUMMY_FREE;
static char     *tabname;

static int      show(int, int, const char * const);
static int      config(char *, char *, char *, int);
static int      getgeom(struct vndgeom *, char *);
__dead static void      usage(void);
static void     show_unused(int);

int
main(int argc, char *argv[])
{
        int ch, rv, action = VND_CONFIG;
        char *end;
        unsigned long cnt;

        while ((ch = getopt(argc, argv, "Fcf:lm:rit:uvz")) != -1) {
                switch (ch) {
                case 'F':
                        force = 1;
                        break;
                case 'c':
                        action = VND_CONFIG;
                        break;
                case 'f':
                        if (setdisktab(optarg) == -1)
                                usage();
                        break;
                case 'l':
                        action = VND_GET;
                        break;
                case 'm':
                        cnt = strtoul(optarg, &end, 10);
                        if (cnt >= INT_MAX || end == optarg || *end != '\0')
                                usage();
                        minimum = (int)cnt;
                        break;
                case 'r':
                        readonly = 1;
                        break;
                case 'i':
                        fileio = 1;
                        break;
                case 't':
                        tabname = optarg;
                        break;
                case 'u':
                        action = VND_UNCONFIG;
                        break;
                case 'v':
                        verbose = 1;
                        break;
                case 'z':
                        compressed = 1;
                        readonly = 1;
                        break;
                default:
                case '?':
                        usage();
                        /* NOTREACHED */
                }
        }
        argc -= optind;
        argv += optind;

        if (action == VND_CONFIG) {
                if ((argc < 2 || argc > 3) ||
                    (argc == 3 && tabname != NULL))
                        usage();
                rv = config(argv[0], argv[1], (argc == 3) ? argv[2] : NULL,
                    action);
        } else if (action == VND_UNCONFIG) {
                if (argc != 1 || tabname != NULL)
                        usage();
                rv = config(argv[0], NULL, NULL, action);
        } else { /* VND_GET */
                int n, vdisk;
                const char *vn;
                char path[64];

                if (argc == 0) {
                        vn = "vnd0";

                        vdisk = opendisk(vn, O_RDONLY, path, sizeof(path), 0);
                        if (vdisk == -1) {
                                if (minimum == 0)
                                        return 1;
                                err(1, "open: %s", vn);
                        }

                        for (n = 0; show(vdisk, n, 0); n++)
                                continue;
                        while (n < minimum)
                                show_unused(n++);
                        close(vdisk);
                        return 0;
                }
                        
                rv = 0;
                while (--argc >= 0) {
                        vn = *argv++;

                        vdisk = opendisk(vn, O_RDONLY, path, sizeof(path), 0);
                        if (vdisk == -1) {
                                warn("open: %s", vn);
                                rv = 1;
                                continue;
                        }

                        if (!show(vdisk, -1, vn))
                                rv = 1;
                        close(vdisk);
                }
        }
        return rv;
}

static void
show_unused(int n)
{
        if (minimum == 0) 
                return;

        printf("vnd%d: not in use\n", n);
}

static int
show(int v, int n, const char * const name)
{
        struct vnd_user vnu;
        char *dev;
        struct statvfs *mnt;
        int i, nmount;

        vnu.vnu_unit = n;
        if (ioctl(v, VNDIOCGET, &vnu) == -1) {
                if (errno != ENXIO) {
                        if (n != -1)
                                err(1, "VNDIOCGET");
                        warn("%s: VNDIOCGET", name);
                }
                return 0;
        }

        if (vnu.vnu_ino == 0) {
                show_unused(vnu.vnu_unit);
                return -1;
        }

        printf("vnd%d: ", vnu.vnu_unit);

        dev = devname(vnu.vnu_dev, S_IFBLK);
        if (dev != NULL)
                nmount = getmntinfo(&mnt, MNT_NOWAIT);
        else {
                mnt = NULL;
                nmount = 0;
        }

        if (mnt != NULL) {
                for (i = 0; i < nmount; i++) {
                        if (strncmp(mnt[i].f_mntfromname, "/dev/", 5) == 0 &&
                            strcmp(mnt[i].f_mntfromname + 5, dev) == 0)
                                break;
                }
                if (i < nmount)
                        printf("%s (%s) ", mnt[i].f_mntonname,
                            mnt[i].f_mntfromname);
                else
                        printf("%s ", dev);
        }
        else if (dev != NULL)
                printf("%s ", dev);
        else
                printf("dev %llu,%llu ",
                    (unsigned long long)major(vnu.vnu_dev),
                    (unsigned long long)minor(vnu.vnu_dev));

        printf("inode %llu\n", (unsigned long long)vnu.vnu_ino);
        return 1;
}

static int
config(char *dev, char *file, char *geom, int action)
{
        struct vnd_ioctl vndio;
        struct disklabel *lp;
        char rdev[MAXPATHLEN + 1];
        int fd, rv;

        fd = opendisk(dev, O_RDWR, rdev, sizeof(rdev), 0);
        if (fd < 0) {
                warn("%s: opendisk", rdev);
                return (1);
        }

        memset(&vndio, 0, sizeof(vndio));
#ifdef __GNUC__
        rv = 0;                 /* XXX */
#endif

        vndio.vnd_file = file;
        if (geom != NULL) {
                rv = getgeom(&vndio.vnd_geom, geom);
                if (rv != 0)
                        errx(1, "invalid geometry: %s", geom);
                vndio.vnd_flags = VNDIOF_HASGEOM;
        } else if (tabname != NULL) {
                lp = getdiskbyname(tabname);
                if (lp == NULL)
                        errx(1, "unknown disk type: %s", tabname);
                vndio.vnd_geom.vng_secsize = lp->d_secsize;
                vndio.vnd_geom.vng_nsectors = lp->d_nsectors;
                vndio.vnd_geom.vng_ntracks = lp->d_ntracks;
                vndio.vnd_geom.vng_ncylinders = lp->d_ncylinders;
                vndio.vnd_flags = VNDIOF_HASGEOM;
        }

        if (readonly)
                vndio.vnd_flags |= VNDIOF_READONLY;

        if (compressed)
                vndio.vnd_flags |= VNF_COMP;

        if (fileio)
                vndio.vnd_flags |= VNDIOF_FILEIO;

        /*
         * Clear (un-configure) the device
         */
        if (action == VND_UNCONFIG) {
                if (force)
                        vndio.vnd_flags |= VNDIOF_FORCE;
                rv = ioctl(fd, VNDIOCCLR, &vndio);
#ifdef VNDIOOCCLR
                if (rv && errno == ENOTTY)
                        rv = ioctl(fd, VNDIOOCCLR, &vndio);
#endif
                if (rv)
                        warn("%s: VNDIOCCLR", rdev);
                else if (verbose)
                        printf("%s: cleared\n", rdev);
        }
        /*
         * Configure the device
         */
        if (action == VND_CONFIG) {
                int     ffd;

                ffd = open(file, readonly ? O_RDONLY : O_RDWR);
                if (ffd < 0) {
                        warn("%s", file);
                        rv = -1;
                } else {
                        (void) close(ffd);

                        rv = ioctl(fd, VNDIOCSET, &vndio);
#ifdef VNDIOOCSET
                        if (rv && errno == ENOTTY) {
                                rv = ioctl(fd, VNDIOOCSET, &vndio);
                                vndio.vnd_size = vndio.vnd_osize;
                        }
#endif
                        if (rv)
                                warn("%s: VNDIOCSET", rdev);
                        else if (verbose) {
                                printf("%s: %" PRIu64 " bytes on %s", rdev,
                                    vndio.vnd_size, file);
                                if (vndio.vnd_flags & VNDIOF_HASGEOM)
                                        printf(" using geometry %d/%d/%d/%d",
                                            vndio.vnd_geom.vng_secsize,
                                            vndio.vnd_geom.vng_nsectors,
                                            vndio.vnd_geom.vng_ntracks,
                                    vndio.vnd_geom.vng_ncylinders);
                                printf("\n");
                        }
                }
        }

        (void) close(fd);
        fflush(stdout);
        return (rv < 0);
}

static int
getgeom(struct vndgeom *vng, char *cp)
{
        char *secsize, *nsectors, *ntracks, *ncylinders;

#define GETARG(arg) \
        do { \
                if (cp == NULL || *cp == '\0') \
                        return (1); \
                arg = strsep(&cp, "/"); \
                if (arg == NULL) \
                        return (1); \
        } while (0)

        GETARG(secsize);
        GETARG(nsectors);
        GETARG(ntracks);
        GETARG(ncylinders);

#undef GETARG

        /* Too many? */
        if (cp != NULL)
                return (1);

#define CVTARG(str, num) \
        do { \
                num = strtol(str, &cp, 10); \
                if (*cp != '\0') \
                        return (1); \
        } while (0)

        CVTARG(secsize, vng->vng_secsize);
        CVTARG(nsectors, vng->vng_nsectors);
        CVTARG(ntracks, vng->vng_ntracks);
        CVTARG(ncylinders, vng->vng_ncylinders);

#undef CVTARG

        return (0);
}

static void
usage(void)
{
        const char *p = getprogname();
        (void)fprintf(stderr, 
            "Usage: %s [-cirvz] [-f dsktab] [-t type] vnode_disk"
                " reg-file [geomspec]\n"
            "       %s -u [-Fv] vnode_disk\n"
            "       %s -l [-m num | vnode_disk...]\n", p, p, p);
        exit(1);
}