root/usr/src/lib/libadm/common/putdgrp.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) 1996-1997, by Sun Microsystems, Inc.
 * All Rights reserved.
 */
/*      Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
/*        All Rights Reserved   */

/*
 * Copyright 2010 Nexenta Systems, Inc.  All rights reserved.
 */

/* LINTLIBRARY */

/*
 * putdgrp.c
 *
 * Global Definitions:
 *      _putdgrptabrec()        Write a device-group record to a stream
 *      _rmdgrptabrec()         Remove a device-group table record
 *      _rmdgrpmems()           Remove specific members from a device group
 *      _adddgrptabrec()        Add a device-group record to the table
 */

/*
 *  G L O B A L   R E F E R E N C E S
 *
 *      Header Files
 *      Externals Referenced
 */

/*
 * Header Files
 *      <sys/types.h>           UNIX System Data Types
 *      <stdio.h>               Standard I/O definitions
 *      <fcntl.h>               Definitions for file control
 *      <errno.h>               Error handling definitions
 *      <string.h>              String Handling Definitions
 *      <unistd.h>              Standard UNIX(r) Definitions
 *      <devmgmt.h>             Device Management Definitions
 *      "devtab.h"              Local Device Management Definitions
 */

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

/*
 *  L O C A L   D E F I N I T I O N S
 *      TDGTABNM        Name of the temporary device-group table (in the
 *                      directory of the existing table)
 */

#define TDGTABNM        "%sdgroup.%6.6d"


/*
 * Static functions
 *      lkdgrptab       Locks the device-group table
 *      unlkdgrptab     Unlocks the device-group table
 *      mkdgrptabent    Builds a device-group table entry from the alias and the
 *                      list of attr=val pairs given
 *      opennewdgrptab  Opens a new device-group table (as a temp file)
 *      mknewdgrptab    Makes the temp device-group table the new dgrptab
 *      rmnewdgrptab    Remove the temporary device-group table and free space
 *                      allocated to the filename of that file.
 */

static  int                     lkdgrptab(char *o_mode, short lktype);
static  int                     unlkdgrptab(void);
static  struct dgrptabent       *mkdgrptabent(char *dgroup, char **members);
static  FILE                    *opennewdgrptab(char **pname);
static  int                     mknewdgrptab(char *tempname);
static  int                     rmnewdgrptab(char *tempname);

/*
 * FILE *opennewdgrptab(pname)
 *      char   **pname
 *
 *      Generates a temporary device-group table name from the existing
 *      device-group table name (in the same directory) and opens that
 *      file for writing.  It puts a pointer to the malloc()ed space
 *      containing the temp device-group table's name at the place
 *      referenced by <pname>.
 *
 *  Arguments:
 *      pname   Pointer to the char * to contain the address of the name
 *              of the temporary file
 *
 *  Returns:  FILE *
 *      A pointer to the opened stream or (FILE *) NULL if an error occurred.
 *      If an error occurred, "errno" will be set to reflect the problem.
 */

static FILE *
opennewdgrptab(char **pname)     /* A(ptr to temp filename's path) */
{
        char   *oldname;                /* Ptr to the dgrptab name */
        char   *buf;                    /* Ptr to the temp file's name */
        char   *dirname;                /* Directory containing dgrptab */
        char   *p;                      /* Ptr to last '/' in dgrptab name */
        int    fd;                      /* Opened file descriptor */
        FILE   *fp;                     /* Opened file pointer */
        struct stat64   sbuf;           /* stat buf for old dgrptab file */


        /* Initializations */
        fp = NULL;

        /* Get the name of the device-group table */
        if (oldname = _dgrptabpath()) {
                /*
                 * It is possible for us to have sufficient
                 * permissions to create the new file without having
                 * sufficient permissions to write the original
                 * dgrptab file.  For consistency with the operations
                 * which modify the original file by writing it
                 * directly we require write permissions for the
                 * original file in order to make a new one.
                 */
                if ((fd = open(oldname, O_WRONLY)) == -1)
                        return (NULL);

                if (fstat64(fd, &sbuf) == -1) {
                        (void) close(fd);
                        return (NULL);
                }
                (void) close(fd);

                /* Get the directory that the device-group table lives in */
                if (p = strrchr(oldname, '/')) {
                        *(p+1) = '\0';
                        dirname = oldname;
                } else
                        dirname = "./";

                /* Get space for the temp dgrptab pathname */
                if (asprintf(&buf, TDGTABNM, dirname, getpid()) >= 0) {
                        /*
                         * Build the name of the temp dgrptab and open
                         * the file.  We must reset the owner, group
                         * and perms to those of the original dgrptab
                         * file.
                         */
                        if (fp = fopen(buf, "w")) {
                                *pname = buf;
                                (void) fchmod(fileno(fp), sbuf.st_mode & 0777);
                                (void) fchown(fileno(fp), sbuf.st_uid,
                                    sbuf.st_gid);
                        } else {
                                free(buf);
                        }
                }

                /* Free the space containing the dgrptab's name */
                free(oldname);
        }

        /* Finished.  Return what we've got */
        return (fp);
}

/*
 *  int rmnewdgrptab(tempname)
 *      char   *tempname
 *
 *      Unlink the temp dgrptab and free the memory allocated to
 *      contain the name of that file
 *
 *  Arguments:
 *      tempname        Name of the temporary file
 *
 *  Returns: int
 *      TRUE if successful, FALSE otherwise
 */

static int
rmnewdgrptab(char *tempname)
{
        /* Automatic data */
        int     noerr;

        /* Unlink the temporary file */
        noerr = (unlink(tempname) == 0);
        free(tempname);

        /* Finished */
        return (noerr);
}

/*
 *  int mknewdgrptab(tempname)
 *      char   *tempname
 *
 *      Make the temporary device-group table the new system
 *      device-group table
 *
 *  Arguments:
 *      tempname        Name of the temporary file
 *
 *  Returns:  int
 *      TRUE if successful, FALSE otherwise
 *
 *  Notes:
 *      - Need to use rename() someday instead of link()/unlink()
 *      - This code is somewhat ineffecient in that asks for the name
 *        of the device-group table more than once.  Done so that we don't
 *        have to manage that space, but this may be somewhat lazy.
 */

static int
mknewdgrptab(char *tempname)            /* Ptr to name of temp dgrp tab */
{
        char   *dgrpname;               /* Ptr to the dgrptab's name */
        int     noerr;                  /* FLAG, TRUE if all's well */

        /* Get the dgrptab's pathname */
        if (dgrpname = _dgrptabpath()) {

                /* Unlink the existing file */
                if (unlink(dgrpname) == 0) {

                        /* Make the temp file the real device-group table */
                        noerr = (link(tempname, dgrpname) == 0) ? TRUE : FALSE;

                        /* Remove the temp file */
                        if (noerr)
                                noerr = rmnewdgrptab(tempname);

                } else {
                        noerr = FALSE;  /* unlink() failed */
                }

                /* Free the dgrptab's name */
                free(dgrpname);

        } else {
                noerr = FALSE;  /* dgrptabpath() failed */
        }

        /* Finished.  Return success indicator */
        return (noerr);
}

/*
 * int lkdgrptab(o_mode, lktype)
 *      char   *o_mode
 *      short   lktype
 *
 *      Lock the device-group table for writing.  If it isn't available, it
 *      waits until it is.
 *
 *  Arguments:
 *      o_mode  The open() mode to use when opening the device-group table
 *      lktype  The type of lock to apply
 *
 *  Returns:  int
 *      TRUE if successful, FALSE with errno set otherwise
 */

static int
lkdgrptab(
        char   *o_mode,                         /* Open mode */
        short   lktype)                         /* Lock type */
{
        /* Automatic data */
        struct flock    lockinfo;               /* File locking structure */
        int             noerr;                  /* FLAG, TRUE if no error */
        int             olderrno;               /* Former value of errno */


        /* Close the device-group table (if it's open) */
        _enddgrptab();

        /* Open the device-group table for read/append */
        noerr = TRUE;
        if (_opendgrptab(o_mode)) {

                /*
                 * Lock the device-group table (for writing).  If it's not
                 * available, wait until it is, then close and open the
                 * table (modify and delete change the table!) and try
                 * to lock it again
                 */

                /* Build the locking structure */
                lockinfo.l_type = lktype;
                lockinfo.l_whence = 0;
                lockinfo.l_start = 0L;
                lockinfo.l_len = 0L;
                olderrno = errno;

                /* Keep on going until we lock the file or an error happens */
                while ((fcntl(fileno(oam_dgroup), F_SETLK, &lockinfo) == -1) &&
                    !noerr) {

                        /*
                         * fcntl() failed.  If errno=EACCES, it's
                         * because the file's locked by someone else.
                         * Wait for the file to be unlocked, then
                         * close and reopen the file and try the lock
                         * again.
                         */

                        if (errno == EACCES) {
                                if (fcntl(fileno(oam_dgroup), F_SETLKW,
                                    &lockinfo) == -1)
                                        noerr = FALSE;
                                else {
                                        _enddgrptab();
                                        if (!_opendgrptab(o_mode))
                                                noerr = FALSE;
                                        else
                                                errno = olderrno;
                                }

                        } else
                                noerr = FALSE;  /* fcntl() failed hard */

                }   /* End while (fcntl() && !noerr) */

                /* Don't keep file open if an error happened */
                if (!noerr) _enddgrptab();

        } else
                noerr = FALSE;  /* _opendgrptab() failed */

        /* Done */
        return (noerr);
}

/*
 * int unlkdgrptab()
 *
 *      Unlock the locked device-group table.
 *
 *  Arguments:  None
 *
 *  Returns:  int
 *      Whatever fcntl() returns...
 */

static int
unlkdgrptab(void)
{
        /* Automatic data */
        struct flock    lockinfo;               /* Locking structure */
        int             noerr;                  /* FLAG, TRUE if all's well */

        /* Build the locking structure */
        lockinfo.l_type = F_UNLCK;              /* Lock type */
        lockinfo.l_whence = 0;                  /* Count from top of file */
        lockinfo.l_start = 0L;                  /* From beginning */
        lockinfo.l_len = 0L;                    /* Length of locked data */

        /* Unlock it */
        noerr = (fcntl(fileno(oam_dgroup), F_SETLK, &lockinfo) != -1);
        _enddgrptab();

        /* Finished */
        return (noerr);
}

/*
 * struct dgrptabent *mkdgrptabent(dgroup, members)
 *      char   *dgroup
 *      char  **members
 *
 *      This function builds a struct dgrptabent structure describing the
 *      device-group <dgroup> so that it contains the members in the
 *      membership list <members>.
 *
 *  Arguments:
 *      dgroup          The device-group being added to the device-group table
 *      members         The members of the device-group
 *
 *  Returns:  struct dgrptabent *
 *      A completed struct dgrptabent structure containing the description
 *      of the device group.  The structure, and all of the data in the
 *      structure are each in space allocated using the malloc() function
 *      and should be freed using the free() function (or the _freedgrptabent()
 *      function.
 */

static struct dgrptabent *
mkdgrptabent(
        char   *dgroup,         /* Device-group being created (or modified) */
        char  **members)        /* Members to add to that entry */
{
        /* Automatic data */
        struct dgrptabent       *ent;   /* Ptr to struct we're making */
        struct member           *prev;  /* Ptr to prev attr/val struct */
        struct member           *member;        /* Ptr to current struct */
        char                    **pp;   /* Ptr into list of ptrs */
        int                     noerr;  /* TRUE if all's well */


        /* No problems (yet) */
        noerr = TRUE;

        /* Get space for the structure */
        if (ent = malloc(sizeof (struct dgrptabent))) {

            /* Fill in default values */
            ent->name = NULL;                           /* alias */
            ent->entryno = 0;                           /* Entry no. */
            ent->comment = FALSE;                       /* data rec */
            ent->dataspace = NULL;                      /* string */
            ent->membership = NULL;                     /* attr list */

            /* Fill in the device-group name */
            if (ent->name = malloc(strlen(dgroup)+1)) {
                (void) strcpy(ent->name, dgroup);

                /* Add membership to the structure */
                prev = NULL;
                if ((pp = members) != NULL)
                    while (*pp && noerr) {

                    if (member = malloc(sizeof (struct member))) {

                        if (member->name = malloc(strlen(*pp)+1)) {
                            (void) strcpy(member->name, *pp);
                            if (prev) prev->next = member;
                            else ent->membership = member;
                            member->next = NULL;
                            prev = member;
                        } else {
                            noerr = FALSE;
                            free(member);
                        }
                    } else noerr = FALSE;
                    pp++;
                }   /* End membership processing loop */

            } else noerr = FALSE;       /* malloc() failed */

                /*
                 * If there was a problem, clean up the mess we've made
                 */

            if (!noerr) {

                _freedgrptabent(ent);
                ent = NULL;

            }   /* if (noerr) */

        } else noerr = FALSE;   /* if (malloc(dgrptabent space)) */

        /* Finished */
        return (ent);
}

/*
 * int _putdgrptabrec(stream, rec)
 *      FILE                   *stream
 *      struct dgrptabent      *rec
 *
 *      Write a device-group table record containing the information in the
 *      struct dgrptab structure <rec> to the current position of the
 *      standard I/O stream <stream>.
 *
 *  Arguments:
 *      stream          The stream to write to
 *      rec             The structure containing the information to write
 *
 *  Returns:  int
 *      The number of characters written or EOF if there was some error.
 */

int
_putdgrptabrec(
        FILE                    *stream,        /* Stream to write to */
        struct dgrptabent       *rec)           /* Record to write */
{
        /* Automatic Data */
        struct member           *mem;           /* Ptr to attr/val pair */
        char                    *buf;           /* Allocated buffer */
        char                    *p;             /* Temp char pointer */
        char                    *q;             /* Temp char pointer */
        int                     count;          /* Number of chars written */
        int                     size;           /* Size of needed buffer */


        /* Comment or data record? */
        if (rec->comment)
                count = fputs(rec->dataspace, stream);
        else {

        /*
         * Record is a data record
         */

            /* Figure out the amount of space the record needs */
            size = (int)strlen(rec->name) + 1;      /* "name:" */
            if ((mem = rec->membership) != NULL)
                do {        /* members */
                    /* "membername " or "membername\n" */
                    size += (int)strlen(mem->name) + 1;
                } while ((mem = mem->next) != NULL);    /* Next attr/val */
            else
                size++;         /* Count trailing '\n' if empty grp */


            /* Alloc space for the record */
            if (buf = malloc((size_t) size+1)) {

                /* Initializations */
                p = buf;

                /* Write the device-group name */
                q = rec->name;
                while (*q) *p++ = *q++;
                *p++ = ':';

                /* Write the membership list */
                if ((mem = rec->membership) != NULL) do {
                    q = mem->name;
                    while (*q) *p++ = *q++;
                    if ((mem = mem->next) != NULL) *p++ = ',';
                } while (mem);

                /* Terminate the record */
                *p++ = '\n';
                *p = '\0';

                /* Write the record */
                count = fputs(buf, stream);
                free(buf);
            } else
                count = EOF;  /* malloc() failed */
        }

        /* Finished */
        return (count);
}

/*
 *  int _adddgrptabrec(dgrp, members)
 *      char   *dgrp
 *      char  **members
 *
 *      If <dgrp> doesn't exist, this function adds a record to the
 *      device-group table for that device-group.  That record will
 *      have the name <dgrp> and will have a membership described in
 *      the list referenced by <members>.  The record is added to the
 *      end of the table.
 *
 *      If <dgrp> already exists in the table, the function adds the
 *      members in the <members> list to the group's membership.
 *
 *  Arguments:
 *      dgrp            The name of the device-group being added to the
 *                      device-group table.
 *      members         A pointer to the first item of the list of members
 *                      in the device-group being added to the table.
 *                      (This value may be (char **) NULL).
 *
 *  Returns:  int
 *      TRUE if successful, FALSE with "errno" set otherwise.
 */

int
_adddgrptabrec(
        char   *dgrp,                   /* Devgrp to add to the table */
        char  **members)                /* Members for that devgrp */
{
        /* Automatic data */
        struct dgrptabent       *ent;           /* Ptr to dev tab entry */
        struct dgrptabent       *new;           /* Ptr to new dev tab info */
        struct dgrptabent       *p;             /* Temp ptr to dev tab info */
        struct member           *pm, *qm, *rm;  /* Tmp ptrs to struct member */
        FILE                    *fd;            /* File descr, temp file */
        char                    *path;          /* Ptr to new devtab name */
        int                     olderrno;       /* Errno on entry */
        int                     noerr;          /* FLAG, TRUE if all's well */


        /* Make a structure describing the new information */
        if ((new = mkdgrptabent(dgrp, members)) == NULL)
            return (FALSE);

        /*
         * Lock the device-group table.  This only returns if the
         * table is locked or some error occurred.  It waits until the
         * table is available.
         */
        if (!lkdgrptab("a+", F_WRLCK)) {
            _freedgrptabent(new);
            return (FALSE);
        }

        /*
         * If the device-group is already in the table, add
         * the specified members
         */

        noerr = TRUE;
        olderrno = errno;
        if (ent = _getdgrprec(dgrp)) {

            /* Any members to add?  If not, do nothing. */
            if (new->membership) {

                /* Any existing members? */
                if ((pm = ent->membership) != NULL) {

                    /* Find the end of the existing membership list */
                    while (pm->next) pm = pm->next;

                    /* Append the new members to the membership list */
                    pm->next = new->membership;

                    /* Remove any duplicates */
                    for (pm = ent->membership; pm; pm = pm->next) {
                        qm = pm;
                        while ((rm = qm->next) != NULL) {
                            if (strcmp(pm->name, rm->name) == 0) {
                                qm->next = rm->next;
                                free(rm->name);
                                free(rm);
                            } else qm = rm;
                        }
                    }
                } else ent->membership = new->membership;

                /* No members in the new list any more */
                new->membership = NULL;

                /*
                 * Make a new device-group table, replacing the
                 * record for the specified device-group
                 */

                _setdgrptab();  /* Rewind existing table */

                /* Open a temp file */
                if (fd = opennewdgrptab(&path)) {

                    /* While there's more records and no error ... */
                    while (((p = _getdgrptabent()) != NULL) && noerr) {

                        /*
                         * If this isn't the record we're replacing,
                         * write it to the temporary file.  Otherwise,
                         * write the updated record
                         */

                        if (ent->entryno != p->entryno)
                                noerr = _putdgrptabrec(fd, p) != EOF;
                        else noerr = _putdgrptabrec(fd, ent) != EOF;
                        _freedgrptabent(p);
                    }

                    /* Fix the files */
                    if (noerr) {
                        (void) fclose(fd);
                        noerr = mknewdgrptab(path);
                    } else {
                        (void) fclose(fd);
                        (void) rmnewdgrptab(path);
                    }
                }   /* if (opennewdgrptab()) */

            }   /* If there's members to add */

            /* Free the memory associated with the updated entry */
            _freedgrptabent(ent);
        }

        /*
         * Otherwise, add the device-group to the end of the table
         */

        else if (errno == EINVAL) {
            errno = olderrno;
            if (fseek(oam_dgroup, 0, SEEK_END) == 0)
                noerr = (_putdgrptabrec(oam_dgroup, new) != EOF);
        } else noerr = FALSE;

        /* Finished */
        (void) unlkdgrptab();           /* Unlock the file */
        _freedgrptabent(new);           /* Free the new dgrptab info struct */
        return (noerr);                 /* Return with success indicator */
}

/*
 * int _rmdgrptabrec(dgrp)
 *      char   *dgrp
 *
 *      This function removes the record in the device-group table
 *      for the specified device-group.
 *
 *  Arguments:
 *      dgrp    The device-group to be removed
 *
 *  Returns:  int
 *      Success indicator:  TRUE if successful, FALSE with errno set otherwise.
 */

int
_rmdgrptabrec(char *dgrp)               /* Device-group to remove */
{
        /* Automatic data */
        struct dgrptabent       *ent;   /* Entry to remove */
        struct dgrptabent       *p;     /* Entry being copied */
        FILE                    *fd;    /* Temp file's file descriptor */
        char                    *path;  /* Pathname of temp file */
        int                     noerr;  /* FLAG, TRUE if all's well */

        noerr = TRUE;
        if (!lkdgrptab("r", F_WRLCK))
                return (FALSE);
        if (ent = _getdgrprec(dgrp)) {
            _setdgrptab();
            if (fd = opennewdgrptab(&path)) {
                while (((p = _getdgrptabent()) != NULL) && noerr) {
                    if (ent->entryno != p->entryno)
                        noerr = _putdgrptabrec(fd, p) != EOF;
                    _freedgrptabent(p);
                }
                if (noerr) {
                    (void) fclose(fd);
                    noerr = mknewdgrptab(path);
                } else {
                    (void) fclose(fd);
                    (void) rmnewdgrptab(path);
                }
            } else noerr = FALSE;
            _freedgrptabent(ent);
        } else noerr = FALSE;
        (void) unlkdgrptab();
        return (noerr);
}

/*
 * int _rmdgrpmems(dgrp, mems, notfounds)
 *      char   *dgrp
 *      char  **mems
 *      char ***notfounds
 *
 *      Remove the specified members from the membership of the specified
 *      device-group.  Any members not found in that device-group are
 *      returned in the list referenced by <notfounds>.
 *
 *  Arguments:
 *      dgrp            The device-group from which members are to be removed
 *      mems            The address of the first element in the list of
 *                      members to remove.  This list is terminated by
 *                      (char *) NULL.
 *      notfounds       The place to put the address of the list of addresses
 *                      referencing the requested members that were not
 *                      members of the specified device-group
 *
 *  Returns: int
 *      TRUE if successful, FALSE with errno set otherwise.
 */

int
_rmdgrpmems(
        char   *dgrp,                   /* Device-group to modify */
        char  **mems,                   /* Members to remove */
        char ***notfounds)              /* Members req'd but not found */
{
        /* Automatic data */
        struct dgrptabent       *ent;   /* Entry to modify */
        struct dgrptabent       *p;     /* Entry being copied */
        struct member           *pm;    /* Ptr to member being examined */
        struct member           *prev;  /* Ptr to previous member */
        char                    **nflst; /* Ptr to not-found list */
        char                    **pnf;  /* Ptr into not-found list */
        char                    **pp;   /* Ptr into members-to-rm list */
        FILE                    *fd;    /* Temp file's file descriptor */
        char                    *path;  /* Pathname of temp file */
        int                     noerr;  /* TRUE if all's well */
        int                     found;  /* TRUE if member is in membership */
        int                     i;      /* Temp counter */

        noerr = TRUE;

        /* Lock the device-group table */
        if (!lkdgrptab("r", F_WRLCK))
                return (FALSE);

        /* Nothing is "not found" yet */
        *notfounds = NULL;

        /* Get the entry we're to modify */
        if (ent = _getdgrprec(dgrp)) {

            /* Allocate space for the not-found list */
            i = 1;
            if (mems)
                for (pp = mems; *pp; pp++)
                        i++;
            if (nflst = malloc(i*sizeof (char *))) {
                pnf = nflst;
                *pnf = NULL;

                /* For each member to remove ... (if any) */
                if (mems)
                    for (pp = mems; *pp; pp++) {

                    found = FALSE;

                    /* Compare against each member in the membership list */
                    pm = ent->membership;
                    prev = NULL;
                    while (pm && !found) {

                        if (strcmp(*pp, pm->name) == 0) {

                            /* Found.  Remove from linked list */
                            if (prev) prev->next = pm->next;
                            else ent->membership = pm->next;
                            if (pm->name) free(pm->name);
                            free(pm);
                            found = TRUE;

                        } else {

                            /* Bump to the next member */
                            prev = pm;
                            pm = pm->next;

                        }

                    }   /* For each member in the group */

                /*
                 * If the requested member-to-remove wasn't found,
                 * add it to the list of not-found members
                 */
                    if (!found) {
                        if (*pnf = malloc(strlen(*pp)+1)) {
                            (void) strcpy(*pnf++, *pp);
                            *pnf = NULL;
                        } else noerr = FALSE;
                    }

                }   /* for (each requested member to remove */

                _setdgrptab();          /* Rewind existing table */

                if (fd = opennewdgrptab(&path)) {
                    while (((p = _getdgrptabent()) != NULL) && noerr) {
                        if (ent->entryno != p->entryno)
                            noerr = _putdgrptabrec(fd, p) != EOF;
                        else noerr = _putdgrptabrec(fd, ent) != EOF;
                        _freedgrptabent(p);
                    }
                    if (noerr) {
                        (void) fclose(fd);
                        noerr = mknewdgrptab(path);
                    } else {
                        (void) fclose(fd);
                        (void) rmnewdgrptab(path);
                    }
                } else noerr = FALSE;   /* if (opennewdgrptab()) */

                /*
                 * If there was no error but there was requested members
                 * that weren't found, set the not-found list and the error
                 * information.  Otherwise, free the not-found list
                 */

                if (noerr && (pnf != nflst)) {
                    *notfounds = nflst;
                    errno = ENODEV;
                    noerr = FALSE;
                } else {
                    for (pnf = nflst; *pnf; pnf++) free(*pnf);
                    free(nflst);
                    if (!noerr) *notfounds = NULL;
                }
            } else noerr = FALSE;

            /* Free the description of the modified device group */
            _freedgrptabent(ent);

        } else noerr = FALSE;    /* _getdgrprec() failed */

        /* Unlock the original device-group table */
        (void) unlkdgrptab();
        return (noerr);
}