root/usr/src/lib/libadm/common/dgrpent.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,1998,2002 by Sun Microsystems, Inc.
 * All rights reserved.
 */

/*LINTLIBRARY*/

/*
 * dgrpent.c
 *
 *  Contains functions that deal with the device-group table and are not for
 *  consumption by the general user population.
 *
 *  Functions defined:
 *      _opendgrptab()          Opens the device-group table for commands
 *      _setdgrptab()           Rewinds the open device table
 *      _enddgrptab()           Closes the open device table
 *      _getdgrptabent()        Gets the next entry in the device table
 *      _freedgrptabent()       Frees memory allocated to a device-table entry
 *      _getdgrprec()           Gets a specific record from the device table
 *      _dgrptabpath()          Gets the pathname of the device group file
 */

/*
 *  Header files
 *      <sys/types.h>   System data types
 *      <unistd.h>      Standard UNIX(r) definitions
 *      <stdio.h>       Standard I/O Definitions
 *      <string.h>      String handling definitions
 *      <ctype.h>       Character types and macros
 *      <errno.h>       Errorcode definitions
 *      <sys/stat.h>    File status information
 *      <devmgmt.h>     Global Device Management definitions
 *      "devtab.h"      Local device table definitions
 */

#include        <sys/types.h>
#include        <unistd.h>
#include        <stdio.h>
#include        <string.h>
#include        <ctype.h>
#include        <errno.h>
#include        <sys/stat.h>
#include        <devmgmt.h>
#include        "devtab.h"
#include        <stdlib.h>

/*
 *  Local definitions
 */


/*
 *  Static data definitions:
 *      leftoff         Addr of char to begin next parse using
 *                      getfld(), getattrval(), getquoted()
 *      recbufsz        The size of the buffer used for reading records
 *      recbuf          Addr of malloc() buffer for reading records
 *      recnum          Record number of next record to read
 *      xtndcnt         Number of times the buffer has been extended
 */

static  char            *leftoff = NULL;
static  int             recbufsz = 0;
static  char            *recbuf = NULL;
static  int             recnum = 0;
static  int             xtndcnt = 0;

/*
 *  void _setdgrptab()
 *
 *      This function rewinds the open device table so that the next
 *      _getdgrptabent() returns the first record in the device table.
 *
 *  Arguments:  None
 *
 *  Returns:  Void
 */

void
_setdgrptab(void)
{
        /*  If the device table file is open, rewind the file  */
        if (oam_dgroup) {
            rewind(oam_dgroup);
            recnum = 0;
        }
}

/*
 *  void _enddgrptab()
 *
 *      This function closes the open device table.  It resets the
 *      open device table external variable to NULL.
 *
 *  Arguments:  None
 *
 *  Returns:  Void
 */

void
_enddgrptab(void)
{
        /*  If the device table file is open, close it  */
        if (oam_dgroup) {
            (void) fclose(oam_dgroup);
            recnum = 0;
            oam_dgroup = NULL;
        }
}

/*
 *  char *getfld(ptr, delims)
 *      char   *ptr
 *      char   *delims
 *
 *  Notes:
 *    - Can't use "strtok()" because of its use of static data.  The caller
 *      may be using strtok() and we'll really mess them up.
 *    - The function returns NULL if it didn't find any token -- '\0' can't
 *      be a delimiter using this algorithm.
 */

static char *
getfld(
        char   *ptr,            /* String to parse */
        char   *delims)         /* List of delimiters */
{
        char   *p, *q;

        /*
         *  Figure out where to start.
         *  If given a pointer, use that.
         *  Otherwise, use where we left off.
         */

        p = ptr ? ptr : leftoff;


        /*
         *  If there's anything to parse, search the string for the first
         *  occurrence of any of the delimiters.  If one is found, change it
         *  to '\0' and remember the place to start for next time.  If not
         *  found, forget the restart address and prepare to return NULL
         */

        if (p) {
            while (*p && isspace((unsigned char)*p)) p++;
            if (*p) {
                q = p;
                while (*q && !strchr(delims, *q)) q++;
                if (*q) {
                    *q++ = '\0';
                    leftoff = q;
                } else leftoff = NULL;
            } else leftoff = p = NULL;
        }

        /*  Finished  */
        return (p);
}

/*
 *  char *getnextrec()
 *
 *      This function gets the next record from the input stream "oam_dgroup"
 *      and puts it in the device-group table record buffer (whose address is
 *      in "recbuf").  If the buffer is not allocated or is too small to
 *      accommodate the record, the function allocates more space to the
 *      buffer.
 *
 *  Arguments:  None
 *
 *  Returns:  char *
 *      The address of the buffer containing the record.
 *
 *  Static Data Referenced:
 *      recbuf          Address of the buffer containing records read from the
 *                      device table file
 *      recbufsz        Current size of the record buffer
 *      xtndcnt         Number of times the record buffer has been extended
 *      oam_dgroup      Device-group table stream, expected to be open for (at
 *                      least) reading
 *
 *  Notes:
 *    - The string returned in the buffer <buf> ALWAYS end in a '\n' (newline)
 *      character followed by a '\0' (null).
 */

static char *
getnextrec(void)
{
        /* Automatic data */
        char            *recp;          /* Value to return */
        char            *p;             /* Temp pointer */
        int             done;           /* TRUE if we're finished */
        int             reclen;         /* Number of chars in record */


        /* If there's no buffer for records, try to get one */
        if (!recbuf) {
            if (recbuf = malloc(DGRP_BUFSIZ)) {
                recbufsz = DGRP_BUFSIZ;
                xtndcnt = 0;
            } else return (NULL);
        }


        /* Get the next record */
        recp = fgets(recbuf, recbufsz, oam_dgroup);
        done = FALSE;

        /* While we've something to return and we're not finished ... */
        while (recp && !done) {

            /* If our return string isn't a null-string ... */
            if ((reclen = (int)strlen(recp)) != 0) {

                /* If we have a complete record, we're finished */
                if (*(recp+reclen-1) == '\n') done = TRUE;
                else while (!done) {

                /*
                 * Need to complete the record.  A complete record is one
                 * which is terminated by a new-line character
                 */

                    /* If the buffer is full, expand it and continue reading */
                    if (reclen == recbufsz-1) {

                        /* Have we reached our maximum extension count? */
                        if (xtndcnt < XTND_MAXCNT) {

                            /* Expand the record buffer */
                            if (p = realloc(recbuf,
                                (size_t)(recbufsz+DGRP_BUFINC))) {

                                /* Update buffer information */
                                xtndcnt++;
                                recbuf = p;
                                recbufsz += DGRP_BUFINC;

                            } else {

                                /* Expansion failed */
                                recp = NULL;
                                done = TRUE;
                            }

                        } else {

                            /* Maximum extend count exceeded.  Insane table */
                            recp = NULL;
                            done = TRUE;
                        }

                    }

                    /* Complete the record */
                    if (!done) {

                        /* Read stuff into the expanded space */
                        if (fgets(recbuf+reclen, recbufsz-reclen, oam_dgroup)) {
                            reclen = (int)strlen(recbuf);
                            recp = recbuf;
                            if (*(recp+reclen-1) == '\n') done = TRUE;
                        } else {
                            /* Read failed, corrupt record? */
                            recp = NULL;
                            done = TRUE;
                        }
                    }

                }   /* End incomplete record handling */

            } else {

                /* Read a null string?  (corrupt table) */
                recp = NULL;
                done = TRUE;
            }

        }   /* while (recp && !done) */

        /* Return what we've got (if anything) */
        return (recp);
}

/*
 *  char *_dgrptabpath()
 *
 *      Get the pathname of the device-group table file
 *
 *  Arguments:  None
 *
 *  Returns:  char *
 *      Returns the pathname to the device group table of (char *) NULL if
 *      there was a problem getting the memory needed to contain the
 *      pathname.
 *
 *  Algorithm:
 *      1.  If OAM_DGRP is defined in the environment and is not
 *          defined as "", it returns the value of that environment
 *          variable.
 *      2.  Otherwise, use the devault pathname (as defined by the
 *          environment variable DGRP_PATH in <devmgmt.h>.
 */


char *
_dgrptabpath(void)
{

        /* Automatic data */
#ifdef  DEBUG
        char            *path;          /* Ptr to path in environment */
#endif
        char            *rtnval;                /* Ptr to value to return */


        /*
         * If compiled with -DDEBUG=1,
         * look for the pathname in the environment
         */

#ifdef  DEBUG
        if (((path = getenv(OAM_DGROUP)) != NULL) && (*path)) {
            if (rtnval = malloc(strlen(path)+1))
                (void) strcpy(rtnval, path);
        } else {
#endif
        /*
         * Use the default name.
         */

            if (rtnval = malloc(strlen(DGRP_PATH)+1))
                (void) strcpy(rtnval, DGRP_PATH);

#ifdef  DEBUG
        }
#endif

        /* Finished */
        return (rtnval);
}

/*
 *  int _opendgrptab(mode)
 *      char   *mode
 *
 *      The _opendgrptab() function opens a device-group table for a command.
 *
 *  Arguments:
 *      mode    The open mode to use to open the file.  (i.e. "r" for
 *              reading, "w" for writing.  See FOPEN(BA_OS) in SVID.)
 *
 *  Returns:  int
 *      TRUE if successful, FALSE otherwise
 */

int
_opendgrptab(char *mode)
{
        /* Automatic data */
        char   *dgrptabname;    /* Ptr to the device-group table name */
        int     rtnval;         /* Value to return */

        rtnval = TRUE;
        if (dgrptabname = _dgrptabpath()) {
            if (oam_dgroup) (void) fclose(oam_dgroup);
            if (oam_dgroup = fopen(dgrptabname, mode)) {
                xtndcnt = 0;
                recnum = 0;
            } else rtnval = FALSE;  /* :-( */
        } else rtnval = FALSE;    /* :-( */
        return (rtnval);
}

/*
 *  struct dgrptabent *_getdgrptabent()
 *
 *      This function returns the next entry in the device-group table.
 *      If no device-group table is open, it opens the standard device-group
 *      table and returns the first record in the table.
 *
 *  Arguments:  None.
 *
 *  Returns:  struct dgrptabent *
 *      Pointer to the next record in the device-group table, or
 *      (struct dgrptabent *) NULL if it was unable to open the file or there
 *      are no more records to read.  "errno" reflects the situation.  If
 *      errno is not changed and the function returns NULL, there are no more
 *      records to read.  If errno is set, it indicates the error.
 *
 *  Notes:
 *    - The caller should set "errno" to 0 before calling this function.
 */

struct dgrptabent *
_getdgrptabent(void)
{
        /*  Automatic data  */
        struct dgrptabent       *ent;           /* Dev table entry structure */
        struct member           *q, *r;         /* Tmps for member structs */
        char                    *record;                /* Record just read */
        char                    *p;             /* Tmp char ptr */
        int                     done;           /* TRUE if built an entry */


        /*  Open the device-group table if it's not already open */
        if (!oam_dgroup)
            if (!_opendgrptab("r"))
                return (NULL);


        /*  Get space for the structure we're returning  */
        if (!(ent = malloc(sizeof (struct dgrptabent)))) {
            return (NULL);
        }

        done = FALSE;
        while (!done && (record = getnextrec())) {

            /* Is this a comment record or a data record */
            if (strchr("#\n", *record) ||
                        isspace((unsigned char)*record)) {

                /*
                 *  Record is a comment record
                 */
                ent->comment = TRUE;
                ent->entryno = recnum++;

                /* Alloc space for the comment and save pointer in struct */
                if (ent->dataspace = malloc(strlen(record)+1)) {
                    (void) strcpy(ent->dataspace, record);
                } else {
                    free(ent);
                    ent = NULL;
                }
                done = TRUE;

            } else {

                /*
                 *  Record is a data record
                 */
                ent->comment = FALSE;

                /* Extract the device-group name */
                if (p = getfld(record, ":")) {

                /* Record is a proper record */
                    done = TRUE;
                    ent->entryno = recnum++;

                    /* Copy device group name into malloc()ed space */
                    if (!(ent->name = malloc(strlen(p)+1))) {

                        free(ent);
                        return (NULL);
                    }
                    (void) strcpy(ent->name, p);

                        /*
                         * Extract the membership from the membership list
                         */

                    /* Get the 1st member */
                    ent->dataspace = NULL;
                    while (((p = getfld(NULL, ",\n")) != NULL) && !(*p))
                        ;
                    if (p) {
                        if (!(q = malloc(sizeof (struct member)))) {

                            free(ent->name);
                            free(ent);
                            return (NULL);
                        }
                        if (!(q->name = malloc(strlen(p)+1))) {
                            free(q);
                            free(ent->name);
                            free((char *)ent);
                            return (NULL);
                        }
                        (void) strcpy(q->name, p);
                        ent->membership = q;
                        q->next = NULL;

                        /* Get the rest of the members */
                        while (p = getfld(NULL, ",\n"))
                            if (*p) {
                                if (!(r = malloc(sizeof (struct member)))) {
                                    for (q = ent->membership; q; q = r) {
                                        free(q->name);
                                        r = q->next;
                                        free(q);
                                    }
                                    free(ent->name);
                                    free(ent);
                                    return (NULL);
                                }
                                if (!(r->name = malloc(strlen(p)+1))) {
                                    free(r);
                                    for (q = ent->membership; q; q = r) {
                                        free(q->name);
                                        r = q->next;
                                        free(q);
                                    }
                                    free(ent->name);
                                    free(ent);
                                    return (NULL);
                                }

                                q->next = r;
                                (void) strcpy(r->name, p);
                                r->next = NULL;
                                q = r;
                            }

                    } else {
                        /* No members */
                        ent->membership = NULL;
                    }

                }   /* record contains a group name */

            }   /* record is a data record */

        }   /* while (!done && there's more records) */


        /*  An entry read?  If not, free alloc'd space and return NULL */
        if (!done) {
            free(ent);
            ent = NULL;
        }

        /* Finis */
        return (ent);
}

/*
 *  void _freedgrptabent(dgrptabent)
 *      struct dgrptabent       *dgrptabent;
 *
 *      This function frees space allocated to a device table entry.
 *
 *  Arguments:
 *      struct dgrptabent *dgrptabent   The structure whose space is to be
 *                                      freed.
 *
 *  Returns:  void
 */

void
_freedgrptabent(struct dgrptabent *ent) /* Structure to free */
{
        /*
         * Automatic data
         */

        struct member  *p;              /* Structure being freed */
        struct member  *q;              /* Next structure to free */

        /*
         *  Free the space allocated to the membership structure.
         */

        if (!ent->comment) {
            if ((q = ent->membership) != NULL) do {
                p = q;
                q = p->next;
                if (p->name) free(p->name);
                free(p);
            } while (q);

            /* Free the device group name */
            if (ent->name) free(ent->name);
        }

        /* Free the membership string */
        if (ent->dataspace) free(ent->dataspace);
}

/*
 *  struct dgrptabent *_getdgrprec(dgroup)
 *      char *dgroup
 *
 *      Thie _getdgrprec() function returns a pointer to a structure that
 *      contains the information in the device-group table entry that describes
 *      the device-group <dgroup>.
 *
 *  Arguments:
 *      char *dgroup    A character-string describing the device-group whose
 *                      record is to be retrieved from the device-group table.
 *
 *  Returns:  struct dgrptabent *
 *      A pointer to a structure describing the device group.
 */

struct dgrptabent *
_getdgrprec(char *dgroup)               /* dgroup to search for */
{
        /*
         *  Automatic data
         */

        struct dgrptabent       *dgrprec;       /* Pointer to current record */
        int                     found;          /* FLAG, TRUE if found */


        /*
         *  Search the device-group table looking for the requested
         *  device group
         */

        _setdgrptab();
        errno = 0;
        found = FALSE;
        while (!found && (dgrprec = _getdgrptabent())) {
            if (!dgrprec->comment && strcmp(dgroup, dgrprec->name) == 0)
                found = TRUE;
            else _freedgrptabent(dgrprec);
        }

        /*  Set up return codes if we've failed  */
        if (!found) {
            if (errno == 0) errno = EINVAL;
            dgrprec = NULL;
        }

        /*  Finis  */
        return (dgrprec);
}