root/usr/src/cmd/fs.d/ufs/lockfs/lockfs.c
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

/*
 * lockfs
 *      user interface to lockfs functionality
 */
#include <sys/types.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/mntent.h>
#include <sys/mnttab.h>
#include <errno.h>
#include <sys/lockfs.h>
#include <sys/filio.h>

#define bzero(s, n)     memset(s, 0, n);

/*
 * command line processing
 */
extern char     *optarg;
extern int      optind;
extern int      opterr;

extern void exit();

static void exitusage();
static void printstatusline(char *, char *, char *);
static void printstatus(char *);
static void flushfs(char *);
static void lockfs(char *);
static void getmntnames();
static void getcmdnames(int, char **, int);

/*
 * -a = all
 * -v = verbose
 */
int all         = 0;
int verbose     = 0;

/*
 * exitstatus
 *      0 all ok
 *      1 internal error
 *      2 system call error
 */
int exitstatus  = 0;

/*
 * list of filenames
 */
struct filename {
        struct filename *fn_next;
        char            *fn_name;
};
struct filename *fnanchor       = 0;

/*
 * default request is `file system lock status'
 * default lock type is `unlock'
 * -wnduhfe changes them
 */
int request     = _FIOLFSS;
ushort_t        lock    = LOCKFS_ULOCK;

/*
 * default comment is null
 *      -c changes it
 */
caddr_t comment = 0;
ulong_t comlen  = 0;

/*
 * for prettyprint
 */
int firsttime   = 0;

/*
 * no unlocks printed
 */
int no_unlocks_printed  = 0;

/*
 * file system was modified during hlock/wlock/elock
 */
#define LOCKWARN(FN, S) \
{ \
        if (verbose) \
                printf("WARNING: %s was modified while %s locked\n", FN, S); \
        exitstatus = 2; \
}

/*
 * forward reference
 */
char    *malloc();

int
main(int argc, char *argv[])
{
        int             c;
        struct filename *fnp;

        exitstatus = 0;

        /*
         * process command line
         */
        opterr = 0;
        optarg = 0;

        while ((c = getopt(argc, argv, "vfwnduheac:")) != -1)
                switch (c) {
                case 'v':
                        verbose = 1;
                        break;
                case 'f':
                        request = _FIOFFS;
                        break;
                case 'w':
                        lock    = LOCKFS_WLOCK;
                        request = _FIOLFS;
                        break;
                case 'n':
                        lock    = LOCKFS_NLOCK;
                        request = _FIOLFS;
                        break;
                case 'd':
                        lock    = LOCKFS_DLOCK;
                        request = _FIOLFS;
                        break;
                case 'h':
                        lock    = LOCKFS_HLOCK;
                        request = _FIOLFS;
                        break;
                case 'e':
                        lock    = LOCKFS_ELOCK;
                        request = _FIOLFS;
                        break;
                case 'u':
                        lock    = LOCKFS_ULOCK;
                        request = _FIOLFS;
                        break;
                case 'a':
                        all = 1;
                        break;
                case 'c':
                        comment = optarg;
                        comlen  = strlen(optarg)+1;
                        request = _FIOLFS;
                        break;
                default:
                        exitusage();
                        break;
                }

        if (argc == 1) {
                no_unlocks_printed = 1;
                all = 1;
        }

        if (all)
                /*
                 * use /etc/mtab
                 */
                getmntnames();
        else
                /*
                 * use command line
                 */
                getcmdnames(argc, argv, optind);

        /*
         * for each filename, doit
         */
        for (fnp = fnanchor; fnp; fnp = fnp->fn_next) {
                switch (request) {
                case _FIOLFSS:
                        printstatus(fnp->fn_name);
                        break;
                case _FIOLFS:
                        lockfs(fnp->fn_name);
                        break;
                case _FIOFFS:
                        flushfs(fnp->fn_name);
                        break;
                default:
                        break;
                }
        }

        /*
         * all done
         */
        return (exitstatus);
}
/*
 * exitusage
 *      bad command line, give hint
 */
void
exitusage()
{
        printf("usage: lockfs [-dfhnuw] [-c string] [-a] [file system ...]\n");
        exit(1);
}
/*
 * printstatusline
 *      prettyprint the status line
 */
void
printstatusline(char *fn, char *locktype, char *comment)
{
        if (firsttime++ == 0)
                printf("%-20s %-10s %s\n", "Filesystem", "Locktype", "Comment");
        printf("%-20s %-10s %s\n", fn, locktype, comment);
}
/*
 * printstatus
 *      get and prettyprint file system lock status
 */
void
printstatus(char *fn)
{
        int             fd;
        int             fsmod   = 0;
        char            *locktype;
        char            commentbuffer[LOCKFS_MAXCOMMENTLEN+1];
        struct lockfs   lf;

        fd = open64(fn, O_RDONLY);
        if (fd == -1) {
                if (errno == EIO)
                        printstatusline(fn, "EIO", "May be hard locked");
                else
                        perror(fn);
                exitstatus = 2;
                return;
        }

        bzero((caddr_t)&lf, sizeof (struct lockfs));

        lf.lf_flags   = LOCKFS_MOD;
        lf.lf_comlen  = LOCKFS_MAXCOMMENTLEN;
        lf.lf_comment = commentbuffer;

        if (ioctl(fd, _FIOLFSS, &lf) == -1) {
                perror(fn);
                close(fd);
                exitstatus = 2;
                return;
        }
        switch (lf.lf_lock) {
        case LOCKFS_ULOCK:
                if (no_unlocks_printed)
                        goto out;
                if (LOCKFS_IS_BUSY(&lf))
                        locktype = "(unlock)";
                else
                        locktype = "unlock";
                break;
        case LOCKFS_WLOCK:
                if (LOCKFS_IS_BUSY(&lf))
                        locktype = "(write)";
                else {
                        locktype = "write";
                        fsmod = LOCKFS_IS_MOD(&lf);
                }
                break;
        case LOCKFS_NLOCK:
                if (LOCKFS_IS_BUSY(&lf))
                        locktype = "(name)";
                else
                        locktype = "name";
                break;
        case LOCKFS_DLOCK:
                locktype = "delete";
                if (LOCKFS_IS_BUSY(&lf))
                        locktype = "(delete)";
                else
                        locktype = "delete";
                break;
        case LOCKFS_HLOCK:
                if (LOCKFS_IS_BUSY(&lf))
                        locktype = "(hard)";
                else {
                        locktype = "hard";
                        fsmod = LOCKFS_IS_MOD(&lf);
                }
                break;
        case LOCKFS_ELOCK:
                if (LOCKFS_IS_BUSY(&lf))
                        locktype = "(error)";
                else {
                        locktype = "error";
                        fsmod = LOCKFS_IS_MOD(&lf);
                }
                break;
        default:
                if (LOCKFS_IS_BUSY(&lf))
                        locktype = "(unknown)";
                else
                        locktype = "unknown";
                break;
        }
        lf.lf_comment[lf.lf_comlen] = '\0';
        printstatusline(fn, locktype, lf.lf_comment);
        if (fsmod)
                LOCKWARN(fn, locktype);
out:
        close(fd);
}
/*
 * flushfs
 *      push and invalidate at least the data that is *currently* dirty
 */
void
flushfs(char *fn)
{
        int             fd;

        fd = open64(fn, O_RDONLY);
        if (fd == -1) {
                perror(fn);
                exitstatus = 2;
                return;
        }

        if (ioctl(fd, _FIOFFS, NULL) == -1) {
                perror(fn);
                close(fd);
                exitstatus = 2;
                return;
        }
        close(fd);
}
/*
 * lockfs
 *      lock the file system
 */
void
lockfs(char *fn)
{
        int             fd;
        struct lockfs   lf;

        fd = open64(fn, O_RDONLY);
        if (fd == -1) {
                perror(fn);
                exitstatus = 2;
                return;
        }

        bzero((caddr_t)&lf, sizeof (struct lockfs));

        lf.lf_flags = LOCKFS_MOD;
        if (ioctl(fd, _FIOLFSS, &lf) == -1) {
                perror(fn);
                close(fd);
                exitstatus = 2;
                return;
        }

        if (!LOCKFS_IS_BUSY(&lf) && LOCKFS_IS_MOD(&lf)) {
                if (LOCKFS_IS_HLOCK(&lf))
                        LOCKWARN(fn, "hard");
                if (LOCKFS_IS_ELOCK(&lf))
                        LOCKWARN(fn, "error");
                if (LOCKFS_IS_WLOCK(&lf))
                        LOCKWARN(fn, "write");
        }

        lf.lf_lock      = lock;
        lf.lf_flags     = 0;
        lf.lf_key       = lf.lf_key;
        lf.lf_comment   = comment;
        lf.lf_comlen    = (comment) ? strlen(comment)+1 : 0;

        if (ioctl(fd, _FIOLFS, &lf) == -1) {
                perror(fn);
                close(fd);
                exitstatus = 2;
                return;
        }
        close(fd);
}
/*
 * getmntnames
 *      file names from /etc/mtab
 */
void
getmntnames()
{
        int             fnlen;
        struct filename *fnp;
        struct filename *fnpc;
        FILE            *mnttab;
        struct mnttab   mnt, *mntp = &mnt;

        fnpc = fnanchor;

        if ((mnttab = fopen(MNTTAB, "r")) == NULL) {
                fprintf(stderr, "Can't open %s\n", MNTTAB);
                perror(MNTTAB);
                exit(32);
        }
        while ((getmntent(mnttab, mntp)) == 0) {
                if (strcmp(mntp->mnt_fstype, MNTTYPE_UFS) != 0)
                        continue;
                fnlen = strlen(mntp->mnt_mountp) + 1;
                fnp = (struct filename *)malloc(sizeof (struct filename));
                fnp->fn_name = malloc((uint_t)fnlen);
                strcpy(fnp->fn_name, mntp->mnt_mountp);
                fnp->fn_next = NULL;
                if (fnpc)
                        fnpc->fn_next = fnp;
                else
                        fnanchor = fnp;
                fnpc = fnp;
        }
        fclose(mnttab);
}
/*
 * getcmdnames
 *      file names from command line
 */
void
getcmdnames(int argc, char **argv, int i)
{
        struct filename *fnp;
        struct filename *fnpc;

        for (fnpc = fnanchor; i < argc; ++i) {
                fnp = (struct filename *)malloc(sizeof (struct filename));
                fnp->fn_name = *(argv+i);
                fnp->fn_next = NULL;
                if (fnpc)
                        fnpc->fn_next = fnp;
                else
                        fnanchor = fnp;
                fnpc = fnp;
        }
}