root/usr/src/cmd/allocate/dminfo.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 2006 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#include <locale.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <string.h>
#include <unistd.h>
#include <malloc.h>
#include <memory.h>
#include <sys/param.h>
#include <sys/types.h>
#include <sys/file.h>
#include <fcntl.h>
#include <bsm/devices.h>
#define DMPFILE "/etc/security/device_maps"
#define RETRY_SLEEP     6
#define RETRY_COUNT     10
#define EINVOKE 2
#define EFAIL 1

#if !defined(TEXT_DOMAIN)
#define TEXT_DOMAIN "SUNW_BSM_DMINFO"
#endif

extern off_t lseek();

char    *getdmapfield();
char    *getdmapdfield();
static void     printdmapent();
static void     dmapi_err();

static char     *prog_name;

/*
 * printdmapent(dmapp) prints a devmap_t structure pointed to by dmapp.
 */
static void
printdmapent(dmapp)
        devmap_t *dmapp;
{
        (void) printf("%s:", dmapp->dmap_devname);
        (void) printf("%s:", dmapp->dmap_devtype);
        (void) printf("%s", dmapp->dmap_devlist);
        (void) printf("\n");
}


/*
 * dmapi_err(exit_code,err_msg) prints message pointed to by err_msg to
 * stderr. Then prints usage message to stderr. Then exits program with
 * exit_code.
 *
 */
static void
dmapi_err(int exit_code, char *err_msg)
{
        if (err_msg != NULL) {
                (void) fprintf(stderr, "dmapinfo:%s\n", err_msg);
        }
        if (exit_code == EINVOKE) {
                (void) fprintf(stderr,
                        "Usage: %s [-v] [-a] [-f filename] %s\n",
                        prog_name,
                        "[-d device ...]");
                (void) fprintf(stderr,
                        "       %s [-v] [-a] [-f filename] %s\n",
                        prog_name,
                        "[-n name ...]");
                (void) fprintf(stderr,
                        "       %s [-v] [-a] [-f filename] %s\n",
                        prog_name,
                        "[-t type ...]");
                (void) fprintf(stderr,
                        "       %s [-v] [-a] [-f filename] %s\n",
                        prog_name,
                        "[-u Entry]");
        }

        exit(exit_code);
}

int
main(int argc, char **argv)
{
        devmap_t *dmapp;
        devmap_t dmap;
        char    *mptr;
        char    *tptr;
        char    *nptr;
        char    *filename = DMPFILE;
        int     name = 0;
        int     device = 0;
        int     file = 0;
        int     verbose = 0;
        int     cntr = 0;
        int     any = 0;
        int     update = 0;
        int     tp = 0;
        int     des;
        int     status;

        /* Internationalization */
        (void) setlocale(LC_ALL, "");
        (void) textdomain(TEXT_DOMAIN);

        /*
         * point prog_name to invocation name
         */
        if ((tptr = strrchr(*argv, '/')) != NULL)
                prog_name = ++tptr;
                else
                prog_name = *argv;
        argc--;
        argv++;
        /*
         * parse arguments
         */
        while ((argc >= 1) && (argv[0][0] == '-')) {
                switch (argv[0][1]) {
                case 'a':
                        any++;
                        break;
                case 'd':
                        if ((name) || (device) || (update) || (tp)) {
                                dmapi_err(EINVOKE,
                                        gettext("option conflict"));
                        }
                        device++;
                        break;
                case 'f':
                        argc--;
                        argv++;
                        if (argc <= 0)
                                dmapi_err(EINVOKE,
                                        gettext("missing file name"));
                        filename = *argv;
                        file++;
                        break;
                case 'n':
                        if ((name) || (device) || (update) || (tp)) {
                                dmapi_err(EINVOKE,
                                        gettext("option conflict"));
                        }
                        name++;
                        break;
                case 't':
                        if ((name) || (device) || (update) || (tp)) {
                                dmapi_err(EINVOKE,
                                        gettext("option conflict"));
                        }
                        tp++;
                        break;
                case 'u':
                        if ((name) || (device) || (update) || (tp)) {
                                dmapi_err(EINVOKE,
                                        gettext("option conflict"));
                        }
                        update++;
                        break;
                case 'v':
                        verbose++;
                        break;
                default:
                        dmapi_err(EINVOKE,
                                gettext("bad option"));
                        break;
                }
                argc--;
                argv++;
        }
        /*
         * -d(device) -n(name) and -u(update) switches require at least one
         * argument.
         */
        if (file)
                setdmapfile(filename);
        if ((device) || (name) || (update) || (tp)) {
                if (argc < 1) {
                        dmapi_err(EINVOKE,
                                gettext("insufficient args for this option"));
                }
        }
        if (update) {
                /*
                 * -u(update) switch requires only one argument
                 */
                if (argc != 1) {
                        dmapi_err(EINVOKE,
                                gettext("too many args for this option"));
                }
                /*
                 * read entry argument from stdin into a devmap_t known as dmap
                 */
                if ((dmap.dmap_devname = getdmapfield(*argv)) == NULL) {
                        dmapi_err(EINVOKE,
                                gettext("Bad dmap_devname in entry argument"));
                }
                if ((dmap.dmap_devtype = getdmapfield(NULL)) ==
                        NULL) {
                        dmapi_err(EINVOKE,
                                gettext("Bad dmap_devtype in entry Argument"));
                }
                if ((dmap.dmap_devlist = getdmapfield(NULL)) ==
                        NULL) {
                        dmapi_err(EINVOKE,
                                gettext("Bad dmap_devlist in entry argument"));
                }
                /*
                 * Find out how long device list is and create a buffer to
                 * hold it.  Then copy it there. This is done since we do not
                 * want to corrupt the existing string.
                 */
                cntr = strlen(dmap.dmap_devlist) + 1;
                mptr = calloc((unsigned)cntr, sizeof (char));
                if (mptr == NULL) {
                        if (verbose) {
                                (void) fprintf(stderr,
                                        gettext(
                                        "dmapinfo: Cannot calloc memory\n"));
                        }
                        exit(1);
                }
                (void) strcpy(mptr, dmap.dmap_devlist);
                /*
                 * open the device maps file for read/ write. We are not
                 * sure we want to write to it yet but we may and this is a
                 * easy way to get the file descriptor. We want the file
                 * descriptor so we can lock the file.
                 */
                if ((des = open(filename, O_RDWR)) < 0) {
                        if (verbose) {
                                (void) fprintf(stderr,
                                gettext("dmapinfo: Cannot open %s\n"),
                                    filename);
                        }
                        exit(1);
                }
                cntr = 0;
#ifdef CMW
                while ((status = flock(des, LOCK_EX | LOCK_NB) == -1) &&
                        (cntr++ < RETRY_COUNT)) {
                        (void) sleep(RETRY_SLEEP);
                }
#else
                while (((status = lockf(des, F_TLOCK, 0)) == -1) &&
                        (cntr++ < RETRY_COUNT)) {
                        (void) sleep(RETRY_SLEEP);
                }
#endif
                if (status == -1) {
                        if (verbose) {
                                (void) fprintf(stderr,
                        gettext("dmapinfo: Cannot lock %s\n"), filename);
                        }
                        exit(1);
                }
                /*
                 * Now that we have the device_maps file then lets check
                 * for previous entrys with the same name.  If it already
                 * exists then we will exit with status of 1.
                 */
                if (verbose) {
                        (void) fprintf(stderr,
                        gettext("dmapinfo: Checking %s for name (%s).\n"),
                                filename, dmap.dmap_devname);
                }
                if (getdmapnam(dmap.dmap_devname) != NULL) {
                        if (verbose) {
                                (void) fprintf(stderr,
                        gettext("dmapinfo: Device name (%s) found in %s.\n"),
                                        dmap.dmap_devname, filename);
                        }
                        exit(1);
                }
                if (verbose) {
                        (void) fprintf(stderr,
                gettext("dmapinfo: Device name (%s) not found in %s.\n"),
                                dmap.dmap_devname, filename);
                }
                /*
                 * We now Know name does not exist and now we need to check
                 * to see if any of the devices in the device list are in the
                 * device maps file. If the already exist then we will exit
                 * with a status of 1.
                 */
                nptr = mptr;
                nptr = getdmapdfield(nptr);
                while (nptr) {
                        if (verbose) {
                                (void) fprintf(stderr,
                                    gettext("dmapinfo: "
                                        "Check %s for device (%s).\n"),
                                    filename, nptr);
                        }
                        if (getdmapdev(nptr) != NULL) {
                                if (verbose) {
                                        (void) fprintf(stderr,
                                            gettext("dmapinfo: "
                                                "Device (%s) found in %s.\n"),
                                            nptr, filename);
                                }
                                exit(1);
                        }
                        if (verbose) {
                                (void) fprintf(stderr,
                                    gettext("dmapinfo: "
                                        "Device (%s) not found in %s.\n"),
                                    nptr, filename);
                        }
                        nptr = getdmapdfield(NULL);
                }
                /*
                 * Good the entry is uniq. So lets find out how long it is
                 * and add it to the end of device maps file in a pretty
                 * way.
                 */
                if (verbose) {
                        (void) fprintf(stderr, "dmapinfo: Adding entry to %s\n",
                                filename);
                        printdmapent(&dmap);
                }
                cntr = strlen(dmap.dmap_devname);
                cntr += strlen(dmap.dmap_devtype);
                cntr += strlen(dmap.dmap_devlist);
                cntr += 15;
                tptr = calloc((unsigned)cntr, sizeof (char));
                if (tptr == NULL) {
                        exit(1);
                }
                (void) strcat(tptr, dmap.dmap_devname);
                (void) strcat(tptr, ":\\\n\t");
                (void) strcat(tptr, dmap.dmap_devtype);
                (void) strcat(tptr, ":\\\n\t");
                (void) strcat(tptr, dmap.dmap_devlist);
                (void) strcat(tptr, ":\\\n\t");
                (void) strcat(tptr, "\n");
                cntr = strlen(tptr);
#ifdef CMW
                if (lseek(des, 0L, L_XTND) == -1L) {
                        exit(1);
                }
#else
                if (lseek(des, 0L, SEEK_END) == -1L) {
                        exit(1);
                }
#endif
                if (write(des, tptr, cntr) == -1) {
                        exit(1);
                }
                if (close(des) == -1) {
                        exit(1);
                }
                if (verbose) {
                        (void) fprintf(stderr, "dmapinfo: Entry added to %s\n",
                                filename);
                }
                exit(0);
        }
        /*
         * Look for devices in device_maps file. If verbose switch is set
         * then print entry(s) found. If "any" switch  is set then, if any
         * device is found will result in a exit status of 0. If "any" switch
         * is not set then, if any device is not will result in a exit status
         * of 1.
         */
        if (device) {
                setdmapent();
                while (argc >= 1) {
                        if ((dmapp = getdmapdev(*argv)) != NULL) {
                                if (verbose) {
                                        printdmapent(dmapp);
                                }
                                cntr++;
                        } else if (any == 0) {
                                enddmapent();
                                exit(1);
                        }
                        argc--;
                        argv++;
                }
                enddmapent();
                if (cntr != 0)
                        exit(0);
                exit(1);
        }
        /*
         * Look for names in device_maps file. If verbose switch is set
         * then print entry(s) found. If "any" switch  is set then, if any
         * name is found will result in a exit status of 0. If "any" switch
         * is not set then, if any name is not will result in a exit status
         * of 1.
         */
        if (name) {
                setdmapent();
                while (argc >= 1) {
                        if ((dmapp = getdmapnam(*argv)) != NULL) {
                                if (verbose) {
                                        printdmapent(dmapp);
                                }
                                cntr++;
                        } else if (any == 0)
                                exit(1);
                        argc--;
                        argv++;
                }
                enddmapent();
                if (cntr != 0)
                        exit(0);
                exit(1);
        }
        /*
         * Read all entrys from device maps file. If verbose flag is set
         * then all the device maps files are printed.  This is useful for
         * piping to grep. Also this option used without the verbose option
         * is useful to check for device maps file and for at least one
         * entry.  If the device maps file is found and there is one entry
         * the return status is 0.
         */
        if (tp) {
                cntr = 0;
                setdmapent();
                while (argc >= 1) {
                        while ((dmapp = getdmaptype(*argv)) != 0) {
                                cntr++;
                                if (verbose) {
                                        printdmapent(dmapp);
                                }
                        }
                        if ((any == 0) && (cntr == 0)) {
                                enddmapent();
                                exit(1);
                        }
                        argc--;
                        argv++;
                }
                enddmapent();
                if (cntr == 0)
                        exit(1);
                exit(0);
        }
        /*
         * Read all entrys from device maps file. If verbose flag is set
         * then all the device maps files are printed.  This is useful for
         * piping to grep. Also this option used without the verbose option
         * is useful to check for device maps file and for atleast one
         * entry.  If the device maps file is found and there is one entry
         * the return status is 0.
         */
        cntr = 0;
        setdmapent();
        while ((dmapp = getdmapent()) != 0) {
                cntr++;
                if (verbose) {
                        printdmapent(dmapp);
                }
        }
        enddmapent();
        if (cntr == 0)
                exit(1);
        return (0);
}