summaryrefslogtreecommitdiff
path: root/usr/src/lib/libadm/common/devtab.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/lib/libadm/common/devtab.c')
-rw-r--r--usr/src/lib/libadm/common/devtab.c1057
1 files changed, 1057 insertions, 0 deletions
diff --git a/usr/src/lib/libadm/common/devtab.c b/usr/src/lib/libadm/common/devtab.c
new file mode 100644
index 0000000000..ae1c812e5d
--- /dev/null
+++ b/usr/src/lib/libadm/common/devtab.c
@@ -0,0 +1,1057 @@
+/*
+ * 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-1998 by Sun Microsystems, Inc.
+ * All Rights reserved.
+ */
+/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+
+#pragma ident "%Z%%M% %I% %E% SMI" /* SVr4.0 1.2 */
+/*LINTLIBRARY*/
+
+/*
+ * devtab.c
+ *
+ * Contains functions that deal with the device table and are not for
+ * consumption by the general user population.
+ *
+ * Functions defined:
+ * _opendevtab() Opens the device table for commands
+ * _setdevtab() Rewinds the open device table
+ * _enddevtab() Closes the open device table
+ * _getdevtabent() Gets the next entry in the device table
+ * _freedevtabent() Frees memory allocated to a device-table entry
+ * _getdevrec() Gets a specific record from the device table
+ * _devtabpath() Get the pathname of the device table file
+ * _validalias() Is a value a valid alias?
+ */
+
+/*
+ * Header files
+ *
+ * <sys/sysmacros.h> System macro definitions
+ * <sys/types.h> System data types
+ * <sys/mkdev.h> Device Macros
+ * <unistd.h> System Symbolic Constants
+ * <stdio.h> Standard I/O definitions
+ * <string.h> String handling definitions
+ * <ctype.h> Character types and macros
+ * <errno.h> Error codes
+ * <sys/stat.h> File status information
+ * <devmgmt.h> Global Device Management definitions
+ * "devtab.h" Local Device Management definitions
+ */
+
+#include <sys/sysmacros.h>
+#include <sys/types.h>
+#ifndef SUNOS41
+#include <sys/mkdev.h>
+#endif
+#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>
+
+/*
+ * Static data definitions:
+ * dtabrecnum Record number of the current record (0 to n-1)
+ * 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
+ * xtndcnt Number of malloc()/realloc() calls on record buffer
+ */
+
+static int xtndcnt = 0;
+static char *recbuf = NULL;
+static int recbufsz = 0;
+
+static char *leftoff = NULL;
+static int dtabrecnum = 0;
+
+/*
+ * int samedev(x, y)
+ * struct stat x, y
+ *
+ * Compares pertinent information in a stat() structure
+ * to see if the two structures describe the same device.
+ * If the file modes are the same and they have the same
+ * file system and i-node (i.e. they're links) or they
+ * are block or character devices and have the same major
+ * and minor device numbers (i.e. "mknod"s for the same
+ * device), it's the same device.
+ *
+ * Returns: int
+ * TRUE if the two structures describe the same device
+ * FALSE otherwise
+ */
+
+static int
+samedev(struct stat64 x, struct stat64 y)
+{
+ int same;
+
+
+ /* If the devices are of the same type ... */
+ if ((x.st_mode & 0170000) == (y.st_mode & 0170000)) {
+
+ /*
+ * If they are described by the same inode on the same device,
+ * the two devices are the same. Otherwise, if the devices are
+ * character-special or block-special devices, try to match by
+ * device type and major and minor device numbers.
+ */
+
+ if ((x.st_dev == y.st_dev) && (x.st_ino == y.st_ino)) same = TRUE;
+ else
+ if (((x.st_mode & 0170000) == 0020000) ||
+ ((x.st_mode & 0170000) == 0060000)) {
+ if ((major(x.st_rdev) == major(y.st_rdev)) &&
+ (minor(x.st_rdev) == minor(y.st_rdev))) same = TRUE;
+ else same = FALSE;
+ } else same = FALSE;
+
+ } else same = FALSE;
+
+ return (same);
+}
+
+/*
+ * void _setdevtab()
+ *
+ * This function rewinds the open device table so that the next
+ * _getdevtabent() returns the first record in the device table.
+ *
+ * Arguments: None
+ *
+ * Returns: Void
+ */
+
+void
+_setdevtab(void)
+{
+ /* If the device table file is open, rewind the file */
+ if (oam_devtab != NULL) {
+ rewind(oam_devtab);
+ dtabrecnum = 0;
+ }
+}
+
+/*
+ * void _enddevtab()
+ *
+ * This function closes the open device table. It resets the
+ * open device table external variable to NULL.
+ *
+ * Arguments: None
+ *
+ * Returns: Void
+ */
+
+void
+_enddevtab(void)
+{
+ /* If the device table file is open, close it */
+ if (oam_devtab != NULL) {
+ (void) fclose(oam_devtab);
+ oam_devtab = NULL;
+ dtabrecnum = 0;
+ }
+}
+
+/*
+ * 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 */
+{
+ int done; /* TRUE if we're finished */
+ char *p, *q; /* Temp pointers */
+
+ /*
+ * 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.
+ * Don't terminate on "escaped" characters.
+ */
+
+ if (p) { /* Anything to do ?? */
+ q = p; /* Where to begin */
+ done = FALSE; /* We're not done yet */
+ while (*q && !done) { /* Any more chars */
+ if (*q == '\\') { /* Escaped ? */
+ if (*(++q)) q++; /* Skip escaped char */
+ } else /* Not escaped */
+ if (!strchr(delims, *q)) q++; /* Skip non-delim */
+ else done = TRUE; /* Otherwise, done */
+ }
+ if (*q) { /* Terminator found? */
+ *q++ = '\0'; /* Null-terminate token */
+ leftoff = q; /* Remember restart pt. */
+ } else
+ leftoff = p = NULL; /* Nothin found or left */
+ }
+
+ /* Finished */
+ return (p); /* Return ptr to token */
+}
+
+/*
+ * char *getquoted(ptr)
+ * char *ptr;
+ *
+ * This function extracts a quoted string from the string pointed
+ * to by <ptr>, or, if <ptr> is NULL, wherever we left off
+ * last time.
+ *
+ * Arguments:
+ * char *ptr Pointer to the character-string to parse, or
+ * (char *) NULL if we're to pick up where we
+ * [getquoted(), getfld(), and getattrval()] left off.
+ *
+ * Returns: char *
+ * The address of malloc()ed space that contains the possibly quoted
+ * string.
+ *
+ * Notes:
+ * - This code only works if it can assume that the last character in
+ * the string it's parsing is a '\n', something that is guarenteed
+ * by the getnextrec() function.
+ */
+
+static char *
+getquoted(char *ptr)
+{
+ /* Automatic data */
+ char *rtn; /* Value to return */
+ char *p, *q; /* Temps */
+
+ /* Figure out where to start off */
+ p = ptr ? ptr : leftoff;
+
+ /* If there's anything to parse and it's a quoted string ... */
+ if ((p) && (*p == '"') && (p = getfld(p+1, "\""))) {
+
+ /* Copy string for the caller */
+ if (rtn = malloc(strlen(p)+1)) { /* Malloc() space */
+ q = rtn; /* Set up temp ptr */
+ do {
+ if (*p == '\\') p++; /* Skip escape */
+ *q++ = *p; /* Copy char */
+ } while (*p++); /* While there's chars */
+ } else leftoff = rtn = NULL;
+ } else leftoff = rtn = NULL;
+
+ /* Fini */
+ return (rtn);
+}
+
+/*
+ * struct attrval *getattrval(ptr)
+ * char *ptr
+ *
+ * This function extracts the next attr=val pair from <ptr> or wherever
+ * getfld() left off...
+ *
+ * Arguments:
+ * char *ptr The string to parse, or (char *) NULL if we're to
+ * begin wherever we left off last time.
+ *
+ * Returns: struct attrval *
+ * The address of a malloc()ed structure containing the attribute and the
+ * value of the attr=val pair extracted.
+ */
+
+static struct attrval *
+getattrval(char *ptr)
+{
+ /* Automatic data */
+ struct attrval *rtn; /* Ptr to struct to return */
+ char *p, *q; /* Temp pointers */
+
+
+ /* Use what's given to us or wherever we left off */
+ p = ptr ? ptr : leftoff;
+
+ /* If there's anything to parse, extract the next attr=val pair */
+ if (p) {
+
+ /* Eat white space */
+ while (*p && isspace((unsigned char)*p)) p++;
+
+ /* Extract the attribute name, if any */
+ if (*p && getfld(p, "=")) {
+
+ /* Allocate space for the structure we're building */
+ if (rtn = malloc(sizeof (struct attrval))) {
+
+ /* Allocate space for the attribute name */
+ if (rtn->attr = malloc(strlen(p)+1)) {
+
+ /* Copy the attribute name into alloc'd space */
+ q = rtn->attr; /* Set up temp ptr */
+ do {
+ if (*p == '\\') p++; /* Skip escape */
+ *q++ = *p; /* Copy char */
+ } while (*p++); /* While more */
+
+ /* Extract the value */
+ if (!(rtn->val = getquoted(NULL))) {
+ /* Error getting value, free resources */
+ free(rtn->attr);
+ free(rtn);
+ leftoff = NULL;
+ rtn = NULL;
+ }
+ } else {
+ /* Error getting space for attribute, free resources */
+ free(rtn);
+ leftoff = NULL;
+ rtn = NULL;
+ }
+
+ } else {
+ /* No space for attr struct */
+ leftoff = NULL;
+ rtn = NULL;
+ }
+
+ } else {
+ /* No attribute name */
+ leftoff = NULL;
+ rtn = NULL;
+ }
+
+ } else {
+ /* Nothing to parse */
+ leftoff = NULL;
+ rtn = NULL;
+ }
+
+ /* Done */
+ return (rtn);
+}
+
+/*
+ * char *getnextrec()
+ *
+ * This function gets the next record from the input stream "oam_devtab"
+ * and puts it in the device-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_devtab Device 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(DTAB_BUFSIZ)) {
+ recbufsz = DTAB_BUFSIZ;
+ xtndcnt = 0;
+ } else return (NULL);
+ }
+
+
+ /* Get the next record */
+ recp = fgets(recbuf, recbufsz, oam_devtab);
+ 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') &&
+ ((reclen == 1) || (*(recp+reclen-2) != '\\'))) done = TRUE;
+ else while (!done) {
+
+ /*
+ * Need to complete the record. A complete record is
+ * one which is terminated by an unescaped 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+DTAB_BUFINC)) {
+
+ /* Update buffer information */
+ xtndcnt++;
+ recbuf = p;
+ recbufsz += DTAB_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_devtab)) {
+ reclen = (int)strlen(recbuf);
+ recp = recbuf;
+ if ((*(recp+reclen-1) == '\n') &&
+ ((reclen == 1) || (*(recp+reclen-2) != '\\')))
+ 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 *_devtabpath()
+ *
+ * Get the pathname of the device table
+ *
+ * Arguments: None
+ *
+ * Returns: char *
+ * Returns the pathname to the device table of NULL if
+ * there was a problem getting the memory needed to contain the
+ * pathname.
+ *
+ * Algorithm:
+ * 1. If OAM_DEVTAB is defined in the environment and is not
+ * defined as "", it returns the value of that environment
+ * variable.
+ * 2. Otherwise, use the value of the environment variable DTAB_PATH.
+ */
+
+
+char *
+_devtabpath(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_DEVTAB)) != NULL) && (*path)) {
+ if (rtnval = malloc(strlen(path)+1))
+ (void) strcpy(rtnval, path);
+ } else {
+#endif
+ /*
+ * Use the standard device table.
+ */
+
+ if (rtnval = malloc(strlen(DTAB_PATH)+1))
+ (void) strcpy(rtnval, DTAB_PATH);
+
+#ifdef DEBUG
+ }
+#endif
+
+ /* Finished */
+ return (rtnval);
+}
+
+/*
+ * int _opendevtab(mode)
+ * char *mode
+ *
+ * The _opendevtab() function opens a device 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 it successfully opens the device table file, FALSE otherwise
+ */
+
+int
+_opendevtab(char *mode)
+{
+ /*
+ * Automatic data
+ */
+
+ char *devtabname; /* Ptr to the device table name */
+ int rtnval; /* Value to return */
+
+
+ rtnval = TRUE;
+ if (devtabname = _devtabpath()) {
+ if (oam_devtab) (void) fclose(oam_devtab);
+ if (oam_devtab = fopen(devtabname, mode))
+ dtabrecnum = 0; /* :-) */
+ else rtnval = FALSE; /* :-( */
+ } else rtnval = FALSE; /* :-( */
+ return (rtnval);
+}
+
+/*
+ * int _validalias(alias)
+ * char *alias
+ *
+ * Determine if <alias> is a valid alias. Returns TRUE if it is
+ * a valid alias, FALSE otherwise.
+ *
+ * Arguments:
+ * alias Value to check out
+ *
+ * Returns: int
+ * TRUE if <alias> is a valid alias, FALSE otherwise.
+ */
+
+int
+_validalias(char *alias) /* Alias to validate */
+{
+ /* Automatic data */
+ char *p; /* Temp pointer */
+ size_t len; /* Length of <alias> */
+ int rtn; /* Value to return */
+
+
+ /* Assume the worst */
+ rtn = FALSE;
+
+ /*
+ * A valid alias contains 0 < i <= 14 characters. The first
+ * must be alphanumeric or "@$_." and the rest must be alphanumeric
+ * or "@#$_+-."
+ */
+
+ /* Check length */
+ if ((alias != NULL) && ((len = strlen(alias)) > 0) && (len <= 14)) {
+
+ /* Check the first character */
+ p = alias;
+ if (isalnum((unsigned char)*p) || strchr("@$_.", *p)) {
+
+ /* Check the rest of the characters */
+ for (p++; *p && (isalnum((unsigned char)*p) ||
+ strchr("@#$_-+.", *p)); p++)
+ ;
+ if (!(*p)) rtn = TRUE;
+ }
+ }
+
+ /* Return indicator... */
+ return (rtn);
+
+} /* int _validalias() */
+
+/*
+ * struct devtabent *_getdevtabent()
+ *
+ * This function returns the next entry in the device table.
+ * If no device table is open, it opens the standard device table
+ * and returns the first record in the table.
+ *
+ * Arguments: None.
+ *
+ * Returns: struct devtabent *
+ * Pointer to the next record in the device table, or
+ * (struct devtabent *) 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 devtabent *
+_getdevtabent(void)
+{
+ /* Automatic data */
+ struct devtabent *ent; /* Ptr to dev table entry structure */
+ struct attrval *attr; /* Ptr to struct for attr/val pair */
+ struct attrval *t; /* Tmp ptr to attr/val struct */
+ char *record; /* Ptr to the record just read */
+ char *p, *q; /* Tmp char ptrs */
+ int done; /* TRUE if we've built an entry */
+
+
+ /* Open the device table if it's not already open */
+ if (oam_devtab == NULL) {
+ if (!_opendevtab("r"))
+ return (NULL);
+ }
+
+ /* Get space for the structure we're returning */
+ if (!(ent = malloc(sizeof (struct devtabent)))) {
+ return (NULL);
+ }
+
+ done = FALSE;
+ while (!done && (record = getnextrec())) {
+
+ /* Save record number in structure */
+ ent->entryno = dtabrecnum++;
+
+ /* Comment record? If so, just save the value and we're through */
+ if (strchr("#\n", *record) || isspace((unsigned char)*record)) {
+ ent->comment = TRUE;
+ done = TRUE;
+ if (ent->attrstr = malloc(strlen(record)+1)) {
+ q = ent->attrstr;
+ p = record;
+ do {
+ if (*p == '\\') p++;
+ *q++ = *p;
+ } while (*p++);
+ } else {
+ free(ent);
+ ent = NULL;
+ }
+ }
+
+ else {
+
+ /* Record is a data record. Parse it. */
+ ent->comment = FALSE;
+ ent->attrstr = NULL; /* For now */
+
+ /* Extract the device alias */
+ if (p = getfld(record, ":")) {
+ if (*p) {
+ if (ent->alias = malloc(strlen(p)+1)) {
+ q = ent->alias;
+ do {
+ if (*p == '\\') p++;
+ *q++ = *p;
+ } while (*p++);
+ }
+ } else ent->alias = NULL;
+
+ /* Extract the character-device name */
+ if ((p = getfld(NULL, ":")) == NULL) {
+ if (ent->alias)
+ free(ent->alias);
+ } else {
+ if (*p) {
+ if (ent->cdevice = malloc(strlen(p)+1)) {
+ q = ent->cdevice;
+ do {
+ if (*p == '\\') p++;
+ *q++ = *p;
+ } while (*p++);
+ }
+ } else ent->cdevice = NULL;
+
+ /* Extract the block-device name */
+ if (!(p = getfld(NULL, ":"))) {
+ if (ent->alias) free(ent->alias);
+ if (ent->cdevice) free(ent->cdevice);
+ } else {
+ if (*p) {
+ if (ent->bdevice = malloc(strlen(p)+1)) {
+ q = ent->bdevice;
+ do {
+ if (*p == '\\') p++;
+ *q++ = *p;
+ } while (*p++);
+ }
+ } else
+ ent->bdevice = NULL;
+
+ /* Extract the pathname */
+ if ((p = getfld(NULL, ":\n")) == NULL) {
+ if (ent->alias) free(ent->alias);
+ if (ent->cdevice) free(ent->cdevice);
+ if (ent->bdevice) free(ent->bdevice);
+ } else {
+ if (*p) {
+ if (ent->pathname = malloc(strlen(p)+1)) {
+ q = ent->pathname;
+ do {
+ if (*p == '\\') p++;
+ *q++ = *p;
+ } while (*p++);
+ }
+ } else
+ ent->pathname = NULL;
+
+ /* Found a valid record */
+ done = TRUE;
+
+ /*
+ * Extract attributes, build a linked list of
+ * 'em (may be none)
+ */
+ if (attr = getattrval(NULL)) {
+ ent->attrlist = attr;
+ t = attr;
+ while (attr = getattrval(NULL)) {
+ t->next = attr;
+ t = attr;
+ }
+ t->next = NULL;
+ } else
+ ent->attrlist = NULL;
+
+ } /* pathname extracted */
+ } /* bdevice extracted */
+ } /* cdevice extracted */
+ } /* alias extracted */
+ }
+ } /* !done && record read */
+
+ /* If no entry was read, free space allocated to the structure */
+ if (!done) {
+ free(ent);
+ ent = NULL;
+ }
+
+ return (ent);
+}
+
+/*
+ * void _freedevtabent(devtabent)
+ * struct devtabent *devtabent;
+ *
+ * This function frees space allocated to a device table entry.
+ *
+ * Arguments:
+ * struct devtabent *devtabent The structure whose space is to be
+ * freed.
+ *
+ * Returns: void
+ */
+
+void
+_freedevtabent(struct devtabent *ent)
+{
+ /*
+ * Automatic data
+ */
+
+ struct attrval *p; /* Structure being freed */
+ struct attrval *q; /* Next structure to free */
+
+ if (!ent->comment) {
+
+ /*
+ * Free the attribute list. For each item in the attribute
+ * list,
+ * 1. Free the attribute name (always defined),
+ * 2. Free the value (if any -- it's not defined if we're
+ * changing an existing attribute),
+ * 3. Free the space allocated to the structure.
+ */
+
+ q = ent->attrlist;
+ if (q)
+ do {
+ p = q;
+ q = p->next;
+ free(p->attr);
+ if (p->val) free(p->val);
+ free(p);
+ } while (q);
+
+ /* Free the standard fields (alias, cdevice, bdevice, pathname) */
+ if (ent->alias) free(ent->alias);
+ if (ent->cdevice) free(ent->cdevice);
+ if (ent->bdevice) free(ent->bdevice);
+ if (ent->pathname) free(ent->pathname);
+ }
+
+ /* Free the attribute string */
+ if (ent->attrstr) free(ent->attrstr);
+
+ /* Free the space allocated to the structure */
+ free(ent);
+}
+
+/*
+ * struct devtabent *_getdevrec(device)
+ * char *device
+ *
+ * Thie _getdevrec() function returns a pointer to a structure that
+ * contains the information in the device-table entry that describes
+ * the device <device>.
+ *
+ * The device <device> can be a device alias, a pathname contained in
+ * the entry as the "cdevice", "bdevice", or "pathname" attribute,
+ * or a pathname to a device described using the "cdevice", "bdevice",
+ * or "pathname" attribute (depending on whether the pathname references
+ * a character-special file, block-special file, or something else,
+ * respectively.
+ *
+ * Arguments:
+ * char *device A character-string describing the device whose record
+ * is to be retrieved from the device table.
+ *
+ * Returns: struct devtabent *
+ * A pointer to a structure describing the device.
+ *
+ * Notes:
+ * - Someday, add a cache so that repeated requests for the same record
+ * don't require going to the filesystem. (Maybe -- this might belong
+ * in devattr()...)
+ */
+
+struct devtabent *
+_getdevrec(char *device) /* The device to search for */
+{
+ /*
+ * Automatic data
+ */
+
+ struct stat64 devstatbuf; /* Stat struct, <device> */
+ struct stat64 tblstatbuf; /* Stat struct, tbl entry */
+ struct devtabent *devrec; /* Pointer to current record */
+ int found; /* TRUE if record found */
+ int olderrno; /* Old value of errno */
+
+
+ /*
+ * Search the device table looking for the requested device
+ */
+
+ _setdevtab();
+ olderrno = errno;
+ found = FALSE;
+ if ((device != NULL) && !_validalias(device)) {
+ while (!found && (devrec = _getdevtabent())) {
+ if (!devrec->comment) {
+ if (devrec->cdevice)
+ if (strcmp(device, devrec->cdevice) == 0) found = TRUE;
+ if (devrec->bdevice)
+ if (strcmp(device, devrec->bdevice) == 0) found = TRUE;
+ if (devrec->pathname)
+ if (strcmp(device, devrec->pathname) == 0) found = TRUE;
+ } else _freedevtabent(devrec);
+ }
+
+ /*
+ * If the device <device> wasn't named explicitly in the device
+ * table, compare it against like entries by comparing file-
+ * system, major device number, and minor device number
+ */
+
+ if (!found) {
+ _setdevtab();
+
+ /* Status the file <device>. If fails, invalid device */
+ if (stat64(device, &devstatbuf) != 0) errno = ENODEV;
+ else {
+
+ /*
+ * If <device> is a block-special device. See if it is
+ * in the table by matching its file-system indicator
+ * and major/minor device numbers against the
+ * file-system and major/minor device numbers of the
+ * "bdevice" entries.
+ */
+
+ if ((devstatbuf.st_mode & 0170000) == 0020000) {
+ while (!found && (devrec = _getdevtabent())) {
+ if (!devrec->comment &&
+ (devrec->cdevice != NULL))
+ if (stat64(devrec->cdevice, &tblstatbuf) == 0) {
+ if (samedev(tblstatbuf, devstatbuf))
+ found = TRUE;
+ } else {
+ /* Ignore stat() errs */
+ errno = olderrno;
+ }
+ if (!found) _freedevtabent(devrec);
+ }
+ }
+
+ /*
+ * If <device> is a block-special device. See if it is
+ * in the table by matching its file-system indicator
+ * and major/minor device numbers against the
+ * file-system and major/minor device numbers of the
+ * "bdevice" entries.
+ */
+
+ else if ((devstatbuf.st_mode & 0170000) == 0060000) {
+ while (!found && (devrec = _getdevtabent())) {
+ if (!devrec->comment &&
+ (devrec->bdevice != NULL))
+ if (stat64(devrec->bdevice, &tblstatbuf) == 0) {
+ if (samedev(tblstatbuf, devstatbuf))
+ found = TRUE;
+ } else {
+ /* Ignore stat() errs */
+ errno = olderrno;
+ }
+ if (!found) _freedevtabent(devrec);
+ }
+ }
+
+ /*
+ * If <device> is neither a block-special or character-
+ * special device. See if it is in the table by
+ * matching its file-system indicator and major/minor
+ * device numbers against the file-system and
+ * major/minor device numbers of the "pathname" entries.
+ */
+
+ else {
+ while (!found && (devrec = _getdevtabent())) {
+ if (!devrec->comment &&
+ (devrec->pathname != NULL))
+ if (stat64(devrec->pathname,
+ &tblstatbuf) == 0) {
+ if (samedev(tblstatbuf, devstatbuf))
+ found = TRUE;
+ } else {
+ /* Ignore stat() errs */
+ errno = olderrno;
+ }
+ if (!found) _freedevtabent(devrec);
+ }
+ }
+
+ if (!found) {
+ devrec = NULL;
+ errno = ENODEV;
+ }
+
+ } /* End case where stat() on the <device> succeeded */
+
+ } /* End case handling pathname not explicitly in device table */
+
+ } /* End case handling <device> as a fully-qualified pathname */
+
+
+ /*
+ * Otherwise the device <device> is an alias.
+ * Search the table for a record that has as the "alias" attribute
+ * the value <device>.
+ */
+
+ else {
+ while (!found && (devrec = _getdevtabent())) {
+ if (!devrec->comment && (device != NULL) &&
+ strcmp(device, devrec->alias) == 0)
+ found = TRUE;
+ else _freedevtabent(devrec);
+ }
+ if (!found) {
+ devrec = NULL;
+ errno = ENODEV;
+ }
+ }
+
+ /* Fini */
+ return (devrec);
+}