root/usr/src/lib/libadm/common/getvol.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 (c) 1984, 1986, 1987, 1988, 1989 AT&T */
/*        All Rights Reserved   */


/*
 * Copyright (c) 1997, by Sun Microsystems, Inc.
 * All rights reserved.
 */

/*LINTLIBRARY*/

#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <devmgmt.h>
#include "libadm.h"
#include <stdlib.h>

#define LABELSIZ        6
#define BELL    "\007"

#define FORMFS_MSG ",\\n\\ \\ or [f] to format %s and place a filesystem on it"
#define FORMAT_MSG ",\\n\\ \\ or [f] to format the %s"
#define MAKEFS_MSG ",\\n\\ \\ or [m] to place a filesystem on %s"
#define EJECT_MSG  ",\\n\\ \\ or [e] to eject the %s"
#define UNLOAD_MSG ",\\n\\ \\ or [u] to unload/offline the %s"
#define WLABEL_MSG ",\\n\\ \\ or [w] to write a new label on the %s"
#define OLABEL_MSG ",\\n\\ \\ or [o] to use the current label anyway"
#define QUIT_MSG   ",\\n\\ \\ or [q] to quit"

#define ERR_ACCESS      "\n%s (%s) cannot be accessed.\n"
#define ERR_FMT         "\nAttempt to format %s failed.\n"
#define ERR_MKFS        "\nAttempt to place filesystem on %s failed.\n"
#define ERR_REMOVE      "\nExecution of \"removecmd\"[%s] failed.\n"

static void     elabel(void);
static void     doformat(char *, char *, char *);
static void     labelerr(char *, char *);
static int      ckilabel(char *, int);
static int      insert(char *, char *, int, char *);

static char     *cdevice;       /* character device name */
static char     *pname;         /* device presentation name */
static char     *volume;        /* volume name */
static char     origfsname[LABELSIZ+1];
static char     origvolname[LABELSIZ+1];

/*
 * Return:
 *      0 - okay, label matches
 *      1 - device not accessable
 *      2 - unknown device (devattr failed)
 *      3 - user selected quit
 *      4 - label does not match
 */

/*
 * macros from labelit to behave correctly for tape
 * is a kludge, should use devmgmt
 */
#ifdef RT
#define IFTAPE(s) ((strncmp(s, "/dev/mt", 7) == 0) || \
(strncmp(s, "mt", 2) == 0))
#define TAPENAMES "'/dev/mt'"
#else
#define IFTAPE(s) ((strncmp(s, "/dev/rmt", 8) == 0) || \
(strncmp(s, "rmt", 3) == 0) || (strncmp(s, "/dev/rtp", 8) == 0) || \
(strncmp(s, "rtp", 3) == 0))
#define TAPENAMES "'/dev/rmt' or '/dev/rtp'"
#endif

int
getvol(char *device, char *label, int options, char *prompt)
{
        return (_getvol(device, label, options, prompt, NULL));
}

int
_getvol(char *device, char *label, int options, char *prompt, char *norewind)
{
        FILE    *tmp;
        char    *advice, *pt;
        int     n, override;

        cdevice = devattr(device, "cdevice");
        if ((cdevice == NULL) || !cdevice[0]) {
                cdevice = devattr(device, "pathname");
                if ((cdevice == NULL) || !cdevice)
                        return (2);     /* bad device */
        }

        pname = devattr(device, "desc");
        if (pname == NULL) {
                pname = devattr(device, "alias");
                if (!pname)
                        pname = device;
        }

        volume = devattr(device, "volume");

        if (label) {
                (void) strncpy(origfsname, label, LABELSIZ);
                origfsname[LABELSIZ] = '\0';
                if (pt = strchr(origfsname, ',')) {
                        *pt = '\0';
                }
                if (pt = strchr(label, ',')) {
                        (void) strncpy(origvolname, pt+1, LABELSIZ);
                        origvolname[LABELSIZ] = '\0';
                } else
                        origvolname[0] = '\0';
        }

        override = 0;
        for (;;) {
                if (!(options & DM_BATCH) && volume) {
                        n = insert(device, label, options, prompt);
                        if (n < 0)
                                override++;
                        else if (n)
                                return (n);     /* input function failed */
                }

                if ((tmp = fopen(norewind ? norewind : cdevice, "r")) == NULL) {
                        /* device was not accessible */
                        if (options & DM_BATCH)
                                return (1);
                        (void) fprintf(stderr, ERR_ACCESS, pname, cdevice);
                        if ((options & DM_BATCH) || (volume == NULL))
                                return (1);
                        /* display advice on how to ready device */
                        if (advice = devattr(device, "advice"))
                                (void) puttext(stderr, advice, 0, 0);
                        continue;
                }
                (void) fclose(tmp);

                /* check label on device */
                if (label) {
                        if (options & DM_ELABEL)
                                elabel();
                        else {
                                /* check internal label using /etc/labelit */
                                if (ckilabel(label, override)) {
                                        if ((options & DM_BATCH) ||
                                            volume == NULL)
                                                return (4);
                                        continue;
                                }
                        }
                }
                break;
        }
        return (0);
}

static int
ckilabel(char *label, int flag)
{
        FILE    *pp;
        char    *pt, *look, buffer[512];
        char    fsname[LABELSIZ+1], volname[LABELSIZ+1];
        char    *pvolname, *pfsname;
        int     n, c;

        (void) strncpy(fsname, label, LABELSIZ);
        fsname[LABELSIZ] = '\0';
        if (pt = strchr(fsname, ',')) {
                *pt = '\0';
        }
        if (pt = strchr(label, ',')) {
                (void) strncpy(volname, pt+1, LABELSIZ);
                volname[LABELSIZ] = '\0';
        } else
                volname[0] = '\0';

        (void) snprintf(buffer, sizeof (buffer), "/etc/labelit %s", cdevice);
        pp = popen(buffer, "r");
        pt = buffer;
        while ((c = getc(pp)) != EOF)
                *pt++ = (char)c;
        *pt = '\0';
        (void) pclose(pp);

        pt = buffer;
        pfsname = pvolname = NULL;
        look = "Current fsname: ";
        n = (int)strlen(look);
        while (*pt) {
                if (strncmp(pt, look, n) == 0) {
                        *pt = '\0';
                        pt += strlen(look);
                        if (pfsname == NULL) {
                                pfsname = pt;
                                look = ", Current volname: ";
                                n = (int)strlen(look);
                        } else if (pvolname == NULL) {
                                pvolname = pt;
                                look = ", Blocks: ";
                                n = (int)strlen(look);
                        } else
                                break;
                } else
                        pt++;
        }

        if (strcmp(fsname, pfsname) || strcmp(volname, pvolname)) {
                /* mismatched label */
                if (flag) {
                        /* LINTED E_SEC_SPRINTF_UNBOUNDED_COPY */
                        (void) sprintf(label, "%s,%s", pfsname, pvolname);
                } else {
                        labelerr(pfsname, pvolname);
                        return (1);
                }
        }
        return (0);
}

static int
wilabel(char *label)
{
        char    buffer[512];
        char    fsname[LABELSIZ+1];
        char    volname[LABELSIZ+1];
        int     n;

        if (!label || !strlen(origfsname)) {
                if (n = ckstr(fsname, NULL, LABELSIZ, NULL, NULL, NULL,
                                "Enter text for fsname label:"))
                        return (n);
        } else
                (void) strcpy(fsname, origfsname);
        if (!label || !strlen(origvolname)) {
                if (n = ckstr(volname, NULL, LABELSIZ, NULL, NULL, NULL,
                                "Enter text for volume label:"))
                        return (n);
        } else
                (void) strcpy(volname, origvolname);

        if (IFTAPE(cdevice)) {
                (void) snprintf(buffer, sizeof (buffer),
                    "/etc/labelit %s \"%s\" \"%s\" -n 1>&2",
                        cdevice, fsname, volname);
        } else {
                (void) snprintf(buffer, sizeof (buffer),
                    "/etc/labelit %s \"%s\" \"%s\" 1>&2",
                    cdevice, fsname, volname);
        }
        if (system(buffer)) {
                (void) fprintf(stderr, "\nWrite of label to %s failed.", pname);
                return (1);
        }
        if (label)
                /* LINTED E_SEC_SPRINTF_UNBOUNDED_COPY */
                (void) sprintf(label, "%s,%s", fsname, volname);
        return (0);
}

static void
elabel(void)
{
}

static int
insert(char *device, char *label, int options, char *prompt)
{
        int     n;
        char    strval[16], prmpt[BUFSIZ];
        char    *pt, *keyword[10];
        char    *fmtcmd;
        char    *mkfscmd;
        char    *voltxt;
        char    *removecmd;
        char    *dev_type;

        voltxt = (volume ? volume : "volume");

        fmtcmd    = devattr(device, "fmtcmd");
        mkfscmd   = devattr(device, "mkfscmd");
        removecmd = devattr(device, "removecmd");
        dev_type  = devattr(device, "type");

        if (prompt) {
                (void) strlcpy(prmpt, prompt, sizeof (prmpt));
                for (pt = prmpt; *prompt; ) {
                        if ((*prompt == '\\') && (prompt[1] == '%'))
                                prompt++;
                        else if (*prompt == '%') {
                                switch (prompt[1]) {
                                    case 'v':
                                        (void) strcpy(pt, voltxt);
                                        break;

                                    case 'p':
                                        (void) strcpy(pt, pname);
                                        break;

                                    default:
                                        *pt = '\0';
                                        break;
                                }
                                pt = pt + strlen(pt);
                                prompt += 2;
                                continue;
                        }
                        *pt++ = *prompt++;
                }
                *pt = '\0';
        } else {
                /* LINTED E_SEC_SPRINTF_UNBOUNDED_COPY */
                (void) sprintf(prmpt, "Insert a %s into %s.", voltxt, pname);
                if (label && (options & DM_ELABEL)) {
                        (void) strcat(prmpt, " The following external label ");
                        /* LINTED E_SEC_SPRINTF_UNBOUNDED_COPY */
                        (void) sprintf(prmpt+strlen(prmpt),
                                " should appear on the %s:\\n\\t%s",
                                voltxt, label);
                }
                if (label && !(options & DM_ELABEL)) {
                        /* LINTED E_SEC_SPRINTF_UNBOUNDED_COPY */
                        (void) sprintf(prmpt+strlen(prmpt),
                            "  The %s should be internally labeled as follows:",
                            voltxt);
                        /* LINTED E_SEC_SPRINTF_UNBOUNDED_COPY */
                        (void) sprintf(prmpt+strlen(prmpt),
                                "\\n\\t%s\\n", label);
                }
        }

        pt = prompt = prmpt + strlen(prmpt);

        n = 0;
        pt += sprintf(pt, "\\nType [go] when ready");
        keyword[n++] = "go";

        if (options & DM_FORMFS) {
                if (fmtcmd && *fmtcmd && mkfscmd && *mkfscmd) {
                        /* LINTED E_SEC_SPRINTF_UNBOUNDED_COPY */
                        pt += sprintf(pt, FORMFS_MSG, voltxt);
                        keyword[n++] = "f";
                } else if (fmtcmd && *fmtcmd) {
                        /* LINTED E_SEC_SPRINTF_UNBOUNDED_COPY */
                        pt += sprintf(pt, FORMAT_MSG, voltxt);
                        keyword[n++] = "f";
                }
                if (mkfscmd && *mkfscmd) {
                        /* LINTED E_SEC_SPRINTF_UNBOUNDED_COPY */
                        pt += sprintf(pt, MAKEFS_MSG, voltxt);
                        keyword[n++] = "m";
                }
        } else if (options & DM_FORMAT) {
                if (fmtcmd && *fmtcmd) {
                        /* LINTED E_SEC_SPRINTF_UNBOUNDED_COPY */
                        pt += sprintf(pt, FORMAT_MSG, voltxt);
                        keyword[n++] = "f";
                }
        }
        if (options & DM_WLABEL) {
                /* LINTED E_SEC_SPRINTF_UNBOUNDED_COPY */
                pt += sprintf(pt, WLABEL_MSG, voltxt);
                keyword[n++] = "w";
        }
        if (options & DM_OLABEL) {
                pt += sprintf(pt, OLABEL_MSG);
                keyword[n++] = "o";
        }
        if (removecmd && *removecmd && dev_type && *dev_type) {
                if (strcmp(dev_type, "diskette") == 0) {
                        /* LINTED E_SEC_SPRINTF_UNBOUNDED_COPY */
                        pt += sprintf(pt, EJECT_MSG, voltxt);
                        keyword[n++] = "e";
                } else {
                        /* LINTED E_SEC_SPRINTF_UNBOUNDED_COPY */
                        pt += sprintf(pt, UNLOAD_MSG, voltxt);
                        keyword[n++] = "u";
                }
        }
        keyword[n] = NULL;
        if (ckquit)
                pt += sprintf(pt, QUIT_MSG);
        *pt++ = ':';
        *pt = '\0';

        pt = prmpt;
        (void) fprintf(stderr, BELL);
        for (;;) {
                if (n = ckkeywd(strval, keyword, NULL, NULL, NULL, pt))
                        return (n);

                pt = prompt; /* next prompt is only partial */
                if (*strval == 'f') {
                        if (options & DM_FORMFS)
                                doformat(voltxt, fmtcmd, mkfscmd);
                        else
                                doformat(voltxt, fmtcmd, NULL);
                        continue;
                } else if (*strval == 'm') {
                        doformat(voltxt, NULL, mkfscmd);
                        continue;
                } else if (*strval == 'e' || *strval == 'u') {
                        (void) doremovecmd(device, 1);
                        continue;
                } else if (*strval == 'w') {
                        (void) wilabel(label);
                        continue;
                } else if (*strval == 'o')
                        return (-1);
                break;
        }
        return (0);
}

static void
doformat(char *voltxt, char *fmtcmd, char *mkfscmd)
{
        char    buffer[512];

        if (fmtcmd && *fmtcmd) {
                (void) fprintf(stderr, "\t[%s]\n", fmtcmd);
                (void) snprintf(buffer, sizeof (buffer), "(%s) 1>&2", fmtcmd);
                if (system(buffer)) {
                        (void) fprintf(stderr, ERR_FMT, voltxt);
                        return;
                }
        }
        if (mkfscmd && *mkfscmd) {
                (void) fprintf(stderr, "\t[%s]\n", mkfscmd);
                (void) snprintf(buffer, sizeof (buffer), "(%s) 1>&2", mkfscmd);
                if (system(buffer)) {
                        (void) fprintf(stderr, ERR_MKFS, voltxt);
                        return;
                }
        }
}

void
doremovecmd(char *device, int echo)
{
        char    *removecmd;
        char    buffer[512];

        if (device && *device) {
                removecmd = devattr(device, "removecmd");
                if (removecmd && *removecmd) {
                        if (echo)
                                (void) fprintf(stderr, "\t[%s]\n", removecmd);
                        (void) snprintf(buffer, sizeof (buffer),
                            "(%s) 1>&2", removecmd);
                        if (system(buffer)) {
                                if (echo)
                                        (void) fprintf(stderr, ERR_REMOVE,
                                        removecmd);
                                return;
                        }
                }
        }
}

static void
labelerr(char *fsname, char *volname)
{
        (void) fprintf(stderr, "\nLabel incorrect.\n");
        if (volume)
                (void) fprintf(stderr,
                        "The internal label on the inserted %s is\n", volume);
        else
                (void) fprintf(stderr, "The internal label for %s is", pname);
        (void) fprintf(stderr, "\t%s,%s\n", fsname, volname);
}