root/usr/src/lib/libadm/common/getdgrp.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*/

/*
 *  getdgrp.c
 *
 * Contains the following global functions:
 *      getdgrp()       Get the device groups that meet certain criteria.
 */

/*
 *  Header Files Referenced
 *      <sys/types.h>           Data Types
 *      <stdio.h>               Standard I/O definitions
 *      <string.h>              Character-string definitions
 *      <devmgmt.h>             Definitions for accessing device table files
 *      "devtab.h"              Local definitions for device tables
 */

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

/*
 *  Local definitions
 *      struct dgrplist         Structure that makes up the internal device
 *                              group list
 *                              Members:
 *                                  name        Name of the device group
 *                                  next        Pointer to the next in the list
 */

struct dgrplist {
        char                    *name;
        struct dgrplist         *next;
};


/*
 *  Local functions
 *      initdgrplist            Initialize the internal device group list
 *      addtodgrplist           Add a device group to the device group list
 *      isindevlist             Does the device group contain a device?
 *      isincallerslist         Is a device group in the caller's list?
 *      buildreturnlist         Build list of device groups to return
 *      freedgrplist            Free the internal device group list
 */

static  void    initdgrplist(void);
static  int     addtodgrplist(struct dgrptabent *);
static  int     isindevlist(struct dgrptabent *, char **);
static  int     isincallerslist(struct dgrptabent *, char **);
static  char    **buildreturnlist(void);
static  void    freedgrplist(void);


/*
 *  Local data
 *      dgrplistfirst   First (dummy) node in the device group list
 *      dgrplistcount   Number of items in the device group list
 */

static  struct dgrplist dgrplistfirst;
static  int             dgrplistcount;

/*
 * char **getdgrp(dgroups, criteria, options)
 *      char  **dgroups
 *      char  **criteria
 *      int     options
 *
 *      This function compiles a list of device groups containing devices
 *      that meet certain criteria and returns a pointer to the first
 *      item in that list.
 *
 *  Arguments:
 *      dgroups         The list of device groups to choose from or the list
 *                      of device groups to exclude from the list (depends on
 *                      "options"
 *      criteria        The criteria that a device must meet
 *      options         Indicates 1) whether to "and" the criteria or to "or"
 *                      the criteria, 2) indicates whether to limit the
 *                      generated list to "dgroups" or to exclude those
 *                      device-groups from the list, 3) to list all device
 *                      groups even if they don't contain valid devices.
 *
 *  Returns:  char **
 *      A pointer to the first address in the list of addresses of generated
 *      device groups
 */

char **
getdgrp(
        char    **dgroups,      /* List of device groups */
        char    **criteria,     /* List of criteria to meet */
        int     options)        /* Options governing the search */
{
        /*  Automatic data  */
        char                    **devlist;      /* Devices that meet criteria */
        char                    **plist;        /* Device groups to return */
        struct dgrptabent       *dgrp;          /* Dgrp information struct */
        int                     errorflag;      /* TRUE if error occurred */
        int listallflag; /* TRUE if DTAB_LISTALL && (!criteria || !*criteria) */


        /*
         *  Open the device-group table if needed
         */

        if (!oam_dgroup && !_opendgrptab("r"))
                return (NULL);


        /*
         *  Get the list of devices that meet the criteria specified
         *  This step can be skipped if DTAB_LISTALL is requested and
         *  there is no criteria list.
         */

        if (((options & DTAB_LISTALL) == 0) || (criteria && *criteria)) {
            devlist = getdev(NULL, criteria, (options & DTAB_ANDCRITERIA));
            listallflag = FALSE;
        } else {
            devlist = NULL;
            listallflag = TRUE;
        }


        /*
         *  Initialize the device group list (contains the device groups
         *  we're accumulating)
         */

        errorflag = FALSE;
        initdgrplist();


        /*
         *  If no device groups were specified by the caller, accumulate all
         *  device groups
         */

        _setdgrptab();
        if (!dgroups || !(*dgroups)) {
            while (!errorflag && (dgrp = _getdgrptabent())) {
                if (!dgrp->comment && (listallflag ||
                    isindevlist(dgrp, devlist)))
                    errorflag = !addtodgrplist(dgrp);
                _freedgrptabent(dgrp);
            }
        }

        else {

        /*
         *  If the exclusion flag is not set, build a list of device
         *  groups that is a subset of those specified by the caller
         */

            if ((options & DTAB_EXCLUDEFLAG) == 0) {
                while (!errorflag && (dgrp = _getdgrptabent())) {
                    if (!dgrp->comment && isincallerslist(dgrp, dgroups) &&
                        (listallflag || isindevlist(dgrp, devlist))) {
                        errorflag = !addtodgrplist(dgrp);
                    }
                    _freedgrptabent(dgrp);
                }
            }

                /*
                 *  If the exclusion flag is set, build a list of device groups
                 *  that meet the criteria and are not in the list of device
                 *  groups specified by the caller.
                 */
            else {
                while (!errorflag && (dgrp = _getdgrptabent())) {
                    if (!dgrp->comment && !isincallerslist(dgrp, dgroups) &&
                        (listallflag || isindevlist(dgrp, devlist))) {
                        errorflag = !addtodgrplist(dgrp);
                    }
                    _freedgrptabent(dgrp);
                }
            }
        }
        plist = buildreturnlist();
        freedgrplist();
        _enddgrptab();
        return (plist);
}

/*
 *  int initdgrplist()
 *
 *      Initializes the internal device group linked list
 *
 *  Arguments:  None
 *
 *  Returns:  void
 */

static void
initdgrplist(void)
{
        /*  Automatic data  */

        /*
         *  Initialize the structure.  Dummy node points to nothing, count to
         * zero.
         */
        dgrplistcount = 0;
        dgrplistfirst.name = "";
        dgrplistfirst.next = NULL;
}

/*
 *  int addtodgrplist(dgrp)
 *      struct dgrptabent *dgrp
 *
 *      Adds the device group described by the "dgrp" structure to the
 *      internal list of device-groups we're accumulating.
 *
 *  Arguments:
 *      dgrp    Describes the device-group we're adding
 *
 *  Returns: int
 *      TRUE if successful, FALSE otherwise
 */

static int
addtodgrplist(struct dgrptabent *dgrp)
{
        /*  Automatic data  */
        struct dgrplist *newnode;       /* Allocated node */
        struct dgrplist *p;             /* Running dgrp list ptr */
        struct dgrplist *q;             /* Another Running dgrp list ptr */
        char            *newstr;        /* Space for the dgroup name */
        int             errorflag;      /* TRUE if error */
        int             cmpval;         /* Value from strcmp() */

        /*  No errors seen yet  */
        errorflag = FALSE;

        /*  Find where we're supposed to insert this item in the list  */
        q = &dgrplistfirst;
        p = q->next;
        while (p && ((cmpval = strcmp(p->name, dgrp->name)) < 0)) {
            q = p;
            p = p->next;
        }

        /*  If the item isn't already in the list, insert it  */
        if ((p == NULL) || (cmpval != 0)) {

            /* Allocate space for the structure */
            newnode = malloc(sizeof (struct dgrplist));
            if (newnode) {

                /* Allocate space for the device group name */
                if (newstr = malloc(strlen(dgrp->name)+1)) {

                    /* Link the new structure into the list */
                    newnode->name = strcpy(newstr, dgrp->name);
                    newnode->next = p;
                    q->next = newnode;
                    dgrplistcount++;
                } else {
                    /* No space for the string.  Clean up */
                    errorflag = TRUE;
                    free(newnode);
                }
            } else errorflag = TRUE;
        }

        /* Return a value that indicates whether we've had an error */
        return (!errorflag);
}

/*
 *  int isindevlist(dgrp, devlist)
 *      struct dgrptabent *dgrp
 *      char             **devlist
 *
 *      This function searches the device membership list of the device
 *      group <dgrp> for any of the devices listed in the list of devices
 *      <devlist>.  It returns TRUE if at least one device in <devlist> is
 *      found in <dgrp>, otherwise it returns false.
 *
 *  Arguments:
 *      dgrp            The device group to examine
 *      devlist         The list of devices to search for
 *
 *  Returns:  int
 *      TRUE if one of the devices in <devlist> is a member of the device
 *      group <dgrp>, FALSE otherwise
 */

static int
isindevlist(
        struct dgrptabent *dgrp,        /* Dgrp to search for */
        char            **devlist)      /* List of devices to search against */
{
        /*  Automatic data  */
        struct member *pmbr;    /* Next member of the dgrp list */
        char **pdev;            /* Next device in the dev list */
        char *mbralias;         /* The alias of a group member */
        int cmpval;             /* strcmp() result */
        int notfound;           /* TRUE if no mbr of dgrp is in dev list */
        int allocflag;          /* TRUE if the mbralias string is malloc()ed */


        /*
         *  For each device in the device group, search the alphabetically
         *  sorted list of devices for that device.
         */

        notfound = TRUE;
        for (pmbr = dgrp->membership; notfound && pmbr; pmbr = pmbr->next) {

        /*
         * Get the member's alias (we've got it if the member is not a
         * pathname)
         */
            allocflag = (*pmbr->name == '/');
            if (allocflag)
                mbralias = devattr(pmbr->name, DTAB_ALIAS);
            else mbralias = pmbr->name;

            /* If we've got a member alias, search the device list for it */
            if (mbralias)
                for (pdev = devlist; notfound && *pdev; pdev++)

                if ((cmpval = strcmp(mbralias, *pdev)) == 0) notfound = FALSE;
                else if (cmpval < 0)
                        break;  /* Optimization:  alpha sorted list */

                /*
                 * Free the space allocated to the member alias
                 * (if it was allocated above by devattr())
                 */
            if (allocflag) free(mbralias);

        }


        /*
         *  Return a value indicating that we the device group contains
         *  a member that is in the list of devices
         */

        return (!notfound);
}

/*
 * int isincallerslist(dgrp, dgroups)
 *      struct dgrptabent *dgrp
 *      char             **dgroups
 *
 *      This function looks through the "dgroups" list for the device
 *      group described by "dgrp"
 *
 *  Arguments:
 *      dgrp            Device group to search for
 *      dgroups         The address of the first item in the list of device
 *                      groups to search
 *
 *  Returns:  int
 *      TRUE if found, FALSE otherwise
 */

static int
isincallerslist(
        struct dgrptabent *dgrp,        /* Dgrp to search for */
        char            **dgroups)      /* Caller's list of dgroups */
{
        /*  Automatic data  */
        char            **pdgrp;
        int             notfound;

        /*
         *  Search the list of device groups for the name of the device group
         *  in the structure described by <dgrp>.
         */

        /*  Initializations  */
        notfound = TRUE;

        /*  Search the device group list for name of this device group  */
        for (pdgrp = dgroups; notfound && *pdgrp; pdgrp++) {
            if (strcmp(dgrp->name, *pdgrp) == 0) notfound = FALSE;
        }

        /*  Return TRUE if the device group is in the list, FALSE otherwise  */
        return (!notfound);
}

/*
 *  char **buildreturnlist()
 *
 *      This function builds the list of pointers to device groups
 *      to return to the caller from the linked list of device-groups
 *      we've been accumulating.
 *
 *  Arguments:  none
 *
 *  Returns: char **
 *      A pointer to the first element in the malloc()ed list of pointers
 *      to malloc()ed character strings containing device groups which have
 *      member devices which match the criteria
 */

static char **
buildreturnlist(void)
{
        char            **list;         /* List being built */
        char            **pp;           /* Temp ptr within list */
        struct dgrplist *pdgrpent;      /* Ptr into list of dgrps to return */

        /*  Allocate space for the list of pointers to device groups */
        list = malloc((dgrplistcount+1)*sizeof (char *));

        /*
         *  For each item in the device group list, put an entry in the
         *  list of names we're building
         */
        if ((pp = list) != NULL) {
            for (pdgrpent = dgrplistfirst.next; pdgrpent;
                pdgrpent = pdgrpent->next) {

                *pp++ = pdgrpent->name;
            }
            /*  The list ends with a null pointer  */
            *pp = NULL;
        }

        /*  Return a pointer to the allocated list  */
        return (list);
}

/*
 *  void freedgrplist()
 *
 *      This function frees the resources allocated to the internal
 *      linked list of device groups
 *
 *  Arguments:  none
 *
 *  Returns:  void
 */

static void
freedgrplist(void)
{
        struct dgrplist         *pdgrpent;      /* Dgrp to free */
        struct dgrplist         *nextnode;      /* Next one to free */

        for (pdgrpent = dgrplistfirst.next; pdgrpent; pdgrpent = nextnode) {
            nextnode = pdgrpent->next;
            free(pdgrpent);
        }
}