/* * 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 * UNIX System Data Types * Standard I/O definitions * Definitions for file control * Error handling definitions * String Handling Definitions * Standard UNIX(r) Definitions * Device Management Definitions * "devtab.h" Local Device Management Definitions */ #include #include #include #include #include #include #include #include #include #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 . * * 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 so that it contains the members in the * membership list . * * 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 to the current position of the * standard I/O 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 doesn't exist, this function adds a record to the * device-group table for that device-group. That record will * have the name and will have a membership described in * the list referenced by . The record is added to the * end of the table. * * If already exists in the table, the function adds the * members in the 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 . * * 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); }