root/usr/src/lib/libdiskmgt/common/inuse_fs.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 2007 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#include <dirent.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <synch.h>
#include <unistd.h>
#include <sys/errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/vfstab.h>
#include <fcntl.h>
#include <sys/wait.h>
#include <sys/fs/ufs_fs.h>

#include "libdiskmgt.h"
#include "disks_private.h"

/*
 * The list of filesystem heuristic programs.
 */
struct heuristic {
        struct heuristic        *next;
        char                    *prog;
        char                    *type;
};

struct vfstab_list {
        char    *special;
        char    *mountp;
        struct vfstab_list      *next;
};

static struct vfstab_list       *vfstab_listp = NULL;
static  mutex_t vfstab_lock = DEFAULTMUTEX;

static  time_t  timestamp = 0;

static struct heuristic *hlist = NULL;
static int              initialized = 0;
static mutex_t          init_lock = DEFAULTMUTEX;

static int      has_fs(char *prog, char *slice);
static int      load_heuristics();
static int      add_use_record(struct vfstab *vp);
static int      load_vfstab();
static void     free_vfstab(struct vfstab_list *listp);

/*
 * Use the heuristics to check for a filesystem on the slice.
 */
int
inuse_fs(char *slice, nvlist_t *attrs, int *errp)
{
        struct  heuristic       *hp;
        time_t  curr_time;
        int     found = 0;


        *errp = 0;

        if (slice == NULL) {
            return (0);
        }

        /*
         * We get the list of heuristic programs one time.
         */
        (void) mutex_lock(&init_lock);
        if (!initialized) {
            *errp = load_heuristics();

            if (*errp == 0) {
                initialized = 1;
            }
        }
        (void) mutex_unlock(&init_lock);

        /* Run each of the heuristics. */
        for (hp = hlist; hp; hp = hp->next) {
            if (has_fs(hp->prog, slice)) {
                libdiskmgt_add_str(attrs, DM_USED_BY, DM_USE_FS, errp);
                libdiskmgt_add_str(attrs, DM_USED_NAME, hp->type, errp);
                found = 1;
            }
        }

        if (*errp != 0)
                return (found);

        /*
         * Second heuristic used is the check for an entry in vfstab
         */

        (void) mutex_lock(&vfstab_lock);
        curr_time = time(NULL);

        if (timestamp < curr_time && (curr_time - timestamp) > 60) {
                free_vfstab(vfstab_listp);
                *errp = load_vfstab();
                timestamp = curr_time;
        }

        if (*errp == 0) {
            struct vfstab_list  *listp;
            listp = vfstab_listp;

            while (listp != NULL) {
                if (strcmp(slice, listp->special) == 0) {
                    char *mountp = "";

                    if (listp->mountp != NULL)
                        mountp = listp->mountp;

                    libdiskmgt_add_str(attrs, DM_USED_BY, DM_USE_VFSTAB, errp);
                    libdiskmgt_add_str(attrs, DM_USED_NAME, mountp, errp);
                    found = 1;
                }
                listp = listp->next;
            }
        }
        (void) mutex_unlock(&vfstab_lock);
        return (found);
}

static int
has_fs(char *prog, char *slice)
{
        pid_t   pid;
        int     loc;
        mode_t  mode = S_IRUSR | S_IWUSR;

        switch ((pid = fork1())) {
        case 0:
            /* child process */

            closefrom(1);
            (void) open("/dev/null", O_WRONLY, mode);
            (void) open("/dev/null", O_WRONLY, mode);
            (void) execl(prog, "fstyp", slice, NULL);
            _exit(1);
            break;

        case -1:
            return (0);

        default:
            /* parent process */
            break;
        }

        (void) waitpid(pid, &loc, 0);

        if (WIFEXITED(loc) && WEXITSTATUS(loc) == 0) {
            return (1);
        }

        return (0);
}

/*
 * Create a list of filesystem heuristic programs.
 */
static int
load_heuristics()
{
        DIR     *dirp;

        if ((dirp = opendir("/usr/lib/fs")) != NULL) {
            struct dirent   *dp;

            while ((dp = readdir(dirp)) != NULL) {
                char            path[MAXPATHLEN];
                struct stat     buf;
                DIR             *subdirp;

                /* skip known dirs */
                if (strcmp(dp->d_name, ".") == 0 ||
                    strcmp(dp->d_name, "..") == 0) {
                    continue;
                }

                /*
                 * Skip checking for ZFS filesystems.  We know that
                 * inuse_zpool() will have already been called, which does a
                 * better job of checking anyway.  More importantly, an unused
                 * hot spare will still claim to have a ZFS filesystem because
                 * it doesn't do the same level of checks.
                 */
                if (strcmp(dp->d_name, "zfs") == 0)
                        continue;

                (void) snprintf(path, sizeof (path), "/usr/lib/fs/%s",
                    dp->d_name);

                if (stat(path, &buf) != 0 || !S_ISDIR(buf.st_mode)) {
                    continue;
                }

                if ((subdirp = opendir(path)) != NULL) {
                    struct dirent   *sdp;

                    while ((sdp = readdir(subdirp)) != NULL) {

                        if (strcmp(sdp->d_name, "fstyp") == 0) {
                            char progpath[MAXPATHLEN];

                            (void) snprintf(progpath, sizeof (progpath),
                                "/usr/lib/fs/%s/fstyp", dp->d_name);

                            if (stat(progpath, &buf) == 0 &&
                                S_ISREG(buf.st_mode)) {

                                struct heuristic *hp;

                                hp = (struct heuristic *)
                                    malloc(sizeof (struct heuristic));

                                if (hp == NULL) {
                                    (void) closedir(subdirp);
                                    (void) closedir(dirp);
                                    return (ENOMEM);
                                }

                                if ((hp->prog = strdup(progpath)) == NULL) {
                                    (void) closedir(subdirp);
                                    (void) closedir(dirp);
                                    return (ENOMEM);
                                }

                                if ((hp->type = strdup(dp->d_name)) == NULL) {
                                    (void) closedir(subdirp);
                                    (void) closedir(dirp);
                                    return (ENOMEM);
                                }

                                hp->next = hlist;
                                hlist = hp;
                            }

                            break;
                        }
                    }

                    (void) closedir(subdirp);
                }
            }

            (void) closedir(dirp);
        }

        return (0);
}

static int
load_vfstab()
{
        FILE    *fp;
        struct  vfstab vp;
        int     status = 1;

        fp = fopen(VFSTAB, "r");
        if (fp != NULL) {
            (void) memset(&vp, 0, sizeof (struct vfstab));
            while (getvfsent(fp, &vp) == 0) {
                    status = add_use_record(&vp);
                    if (status != 0) {
                        (void) fclose(fp);
                        return (status);
                    }
                (void) memset(&vp, 0, sizeof (struct vfstab));
            }
            (void) fclose(fp);
            status = 0;
        }

        return (status);
}

static int
add_use_record(struct vfstab *vp)
{
        struct  vfstab_list     *vfsp;

        vfsp = (struct vfstab_list *)malloc(sizeof (struct vfstab_list));
        if (vfsp == NULL) {
            return (ENOMEM);
        }

        vfsp->special = strdup(vp->vfs_special);
        if (vfsp->special == NULL) {
            free(vfsp);
            return (ENOMEM);
        }

        if (vp->vfs_mountp != NULL) {
            vfsp->mountp = strdup(vp->vfs_mountp);
            if (vfsp->mountp == NULL) {
                free(vfsp);
                return (ENOMEM);
            }
        } else {
            vfsp->mountp = NULL;
        }

        vfsp->next = vfstab_listp;
        vfstab_listp = vfsp;

        return (0);
}

static void
free_vfstab(struct vfstab_list *listp)
{
        struct vfstab_list      *nextp;

        while (listp != NULL) {
            nextp = listp->next;
            free((void *)listp->special);
            free((void *)listp->mountp);
            free((void *)listp);
            listp = nextp;
        }

        vfstab_listp = NULL;
}