root/usr/src/lib/libdiskmgt/common/inuse_lu.c
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (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 2004 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

/*
 * Creates and maintains a short-term cache of live upgrade slices.
 */

#include <dirent.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <synch.h>
#include <sys/errno.h>
#include <sys/param.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>

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

#define TMPNM_SIZE      25

/*
 * The list of live upgrade slices in use.
 */

struct lu_list {
        struct lu_list  *next;
        char            *slice;
        char            *name;
};

static struct lu_list   *lu_listp = NULL;
static time_t           timestamp = 0;
static mutex_t          lu_lock = DEFAULTMUTEX;

static int              add_use_record(char *devname, char *name);
static void             free_lu(struct lu_list *listp);
static int              load_lu();
static int              lustatus(int fd);
static int              lufslist(int fd);
static int              run_cmd(char *path, char *cmd, char *arg, int fd);

/*
 * Search the list of devices under live upgrade for the specified device.
 */
int
inuse_lu(char *slice, nvlist_t *attrs, int *errp)
{
        int             found = 0;
        time_t          curr_time;

        *errp = 0;

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

        /*
         * We don't want to have to re-read the live upgrade config for
         * every slice, but we can't just cache it since there is no event
         * when this changes.  So, we'll keep the config in memory for
         * a short time (1 minute) before reloading it.
         */
        (void) mutex_lock(&lu_lock);

        curr_time = time(NULL);
        if (timestamp < curr_time && (curr_time - timestamp) > 60) {
            free_lu(lu_listp);  /* free old entries */
            lu_listp = NULL;
            *errp = load_lu();  /* load the cache */
            timestamp = curr_time;
        }

        if (*errp == 0) {
            struct lu_list      *listp;

            listp = lu_listp;
            while (listp != NULL) {
                if (strcmp(slice, listp->slice) == 0) {
                    libdiskmgt_add_str(attrs, DM_USED_BY, DM_USE_LU, errp);
                    libdiskmgt_add_str(attrs, DM_USED_NAME, listp->name, errp);
                    found = 1;
                    break;
                }
                listp = listp->next;
            }
        }

        (void) mutex_unlock(&lu_lock);

        return (found);
}

static int
add_use_record(char *devname, char *name)
{
        struct lu_list *sp;

        sp = (struct lu_list *)malloc(sizeof (struct lu_list));
        if (sp == NULL) {
            return (ENOMEM);
        }

        if ((sp->slice = strdup(devname)) == NULL) {
            free(sp);
            return (ENOMEM);
        }

        if ((sp->name = strdup(name)) == NULL) {
            free(sp->slice);
            free(sp);
            return (ENOMEM);
        }

        sp->next = lu_listp;
        lu_listp = sp;

        return (0);
}

/*
 * Free the list of liveupgrade entries.
 */
static void
free_lu(struct lu_list *listp) {

        struct lu_list  *nextp;

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

/*
 * Create a list of live upgrade devices.
 */
static int
load_lu()
{
        char    tmpname[TMPNM_SIZE];
        int     fd;
        int     status = 0;

        (void) strlcpy(tmpname, "/var/run/dm_lu_XXXXXX", TMPNM_SIZE);
        if ((fd = mkstemp(tmpname)) != -1) {
            (void) unlink(tmpname);
            if (run_cmd("/usr/sbin/lustatus", "lustatus", NULL, fd)) {
                status = lustatus(fd);
            } else {
                (void) close(fd);
            }
        }

        return (status);
}

/*
 * The XML generated by the live upgrade commands is not parseable by the
 * standard Solaris XML parser, so we have to do it ourselves.
 */
static int
lufslist(int fd)
{
        FILE    *fp;
        char    line[MAXPATHLEN];
        int     status;

        if ((fp = fdopen(fd, "r")) == NULL) {
            (void) close(fd);
            return (0);
        }

        (void) fseek(fp, 0L, SEEK_SET);
        while (fgets(line, sizeof (line), fp) == line) {
            char *devp;
            char *nmp;
            char *ep;

            if (strncmp(line, "<beFsComponent ", 15) != 0) {
                continue;
            }

            if ((devp = strstr(line, "fsDevice=\"")) == NULL) {
                continue;
            }

            devp = devp + 10;

            if ((ep = strchr(devp, '"')) == NULL) {
                continue;
            }

            *ep = 0;

            /* try to get the mountpoint name */
            if ((nmp = strstr(ep + 1, "mountPoint=\"")) != NULL) {
                nmp = nmp + 12;

                if ((ep = strchr(nmp, '"')) != NULL) {
                    *ep = 0;
                } else {
                    nmp = "";
                }

            } else {
                nmp = "";
            }

            if ((status = add_use_record(devp, nmp)) != 0) {
                break;
            }
        }

        (void) fclose(fp);

        return (status);
}

static int
lustatus(int fd)
{
        FILE    *fp;
        char    line[MAXPATHLEN];
        int     status = 0;

        if ((fp = fdopen(fd, "r")) == NULL) {
            (void) close(fd);
            return (0);
        }

        (void) fseek(fp, 0L, SEEK_SET);
        while (fgets(line, sizeof (line), fp) == line) {
            char        *sp;
            char        *ep;
            char        tmpname[TMPNM_SIZE];
            int         ffd;

            if (strncmp(line, "<beStatus ", 10) != 0) {
                continue;
            }

            if ((sp = strstr(line, "name=\"")) == NULL) {
                continue;
            }

            sp = sp + 6;

            if ((ep = strchr(sp, '"')) == NULL) {
                continue;
            }

            *ep = 0;

            (void) strlcpy(tmpname, "/var/run/dm_lu_XXXXXX", TMPNM_SIZE);
            if ((ffd = mkstemp(tmpname)) != -1) {
                (void) unlink(tmpname);

                if (run_cmd("/usr/sbin/lufslist", "lufslist", sp, ffd) == 0) {
                    (void) close(ffd);
                    break;
                }

                if ((status = lufslist(ffd)) != 0) {
                    break;
                }
            }
        }

        (void) fclose(fp);

        return (status);
}

static int
run_cmd(char *path, char *cmd, char *arg, int fd)
{
        pid_t   pid;
        int     loc;

        /* create the server process */
        switch ((pid = fork1())) {
        case 0:
            /* child process */
            (void) close(1);
            (void) dup(fd);
            (void) close(2);
            (void) dup(fd);
            closefrom(3);
            (void) execl(path, cmd, "-X", arg, NULL);
            _exit(1);
            break;

        case -1:
            return (0);

        default:
            /* parent process */
            break;
        }

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

        /* printf("got 0x%x %d %d\n", loc, WIFEXITED(loc), WEXITSTATUS(loc)); */

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

        return (0);
}