/* * 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*/ /* * getdev.c * * Contents: * getdev() List devices that match certain criteria. */ /* * Header files referenced: * System Data Types * Error handling * File controlling * Character types * String handling * Global device-management def'ns * "devtab.h" Local device-management dev'ns */ #include #include #include #include #include #include #include "devtab.h" #include /* * Local definitions * NULL Nil address * TRUE Boolean TRUE * FALSE Boolean FALSE */ #ifndef NULL #define NULL 0 #endif #ifndef TRUE #define TRUE ('t') #endif #ifndef FALSE #define FALSE 0 #endif /* * Comparison values. These values are placed in the struct srch * structure by buildsearchlist() and are used to compare values * in matches(). * EQUAL Attribute must equal this value * NOTEQUAL Attribute must not equal this value * EXISTS Attribute must exist * NOEXISTS Attribute must not exist * IGNORE Ignore this entry * ENDLIST This entry ends the list */ #define EQUAL 1 #define NOTEQUAL 2 #define EXISTS 3 #define NOEXISTS 4 #define IGNORE 5 #define ENDLIST 0 /* * Structure definitions: * deviceent Defines a device that matches criteria * srch Describes a criteria */ struct deviceent { struct deviceent *next; /* Pointer to next item in the list */ char *name; /* Presentation name of the device */ }; struct srch { char *name; /* Name of field to compare */ char *cmp; /* Value to compare against */ int fcn; /* Type of comparison (see above) */ }; /* * Local functions referenced * oktoaddtolist() Determines if device can be added to the * list by examining the devices list and * the options governing the search * initdevicelist() Initializes the linked list of devices * to be included in the list-to-return * freedevicelist() Frees the resources allocated to the linked * list of devices * addtodevicelist() Adds an entry to the linked list of devices * buildsearchlist() Builds a list of struct srch structures from * the criteria strings * freesearchlist() Frees the resources allocated to the list of * struct srch structures * buildreturnlist() Builds the list of devices to return from the * linked list of devices we've accumulated * makealiaslist() Builds a list of aliases from the list of * devices presented by the caller * freealiaslist() Frees the resources allocated to the list of * devices aliases * getnextmatch() Get the next device that matches the search * criteria * matchallcriteria() See if the device attributes match all of the * search criteria * matchanycriteria() See if the device attributes match any of the * search criteria * matches() See if the criteria and attribute match */ static char *oktoaddtolist(char *, char **, char **, int); static void initdevicelist(void); static void freedevicelist(void); static int addtodevicelist(char *); static struct srch *buildsearchlist(char **); static void freesearchlist(struct srch *); static char **buildreturnlist(void); static char **makealiaslist(char **); static void freealiaslist(char **); static char *getnextmatch(struct srch *, int); static int matchallcriteria(struct devtabent *, struct srch *); static int matchanycriteria(struct devtabent *, struct srch *); static int matches(char *, char *, int); /* * Global Data */ /* * Static Data * devicelisthead The first item (dummy) in the linked list of devices * we're building * devicelist Structure describing the linked list of devices */ static struct deviceent devicelisthead; static struct { struct deviceent *head; int count; } devicelist = {&devicelisthead, 0}; /* * char **getdev(devices, criteria, options) * char **devices * char **criteria * int options * * This function builds a list of devices that match criteria, * governed by the device list. * * Arguments: * devices The list of devices to select from or the list of * devices to exclude, depending on the value of * "options" * criteria The list of criteria governing the device selection * Of the form * options Options controlling the device selection. May require * that a device meet all of the criteria (default is * any one of the criteria), or may require that the * devices in the list of devices be excluded from the * generated list (default is to select only those * devices in the list) * * Returns: char ** * The address of the first item in the list of devices that meet * the selection criteria */ char ** getdev( char **devices, /* List of devices to constrain */ char **criteria, /* List of selection criteria */ int options) /* Options governing the search */ { /* Automatic data */ char **aliases; /* List of constraining devices */ char **returnlist; /* List of ptrs to aliases to return */ struct srch *searchlist; /* Pointer to searching criteria */ char *entry; /* Pointer to alias in record */ int errflag; /* FLAG: TRUE if error */ /* * Initializations */ /* Make sure the exclude/include list is all aliases */ aliases = makealiaslist(devices); if (devices && !aliases) return (NULL); /* Build the search list */ if (criteria) { if (!(searchlist = buildsearchlist(criteria))) return (NULL); } else searchlist = NULL; /* Initialize searching */ initdevicelist(); _setdevtab(); /* * Keep on going until we get no more matches */ errflag = FALSE; while (!errflag && (entry = getnextmatch(searchlist, options))) { if (entry = oktoaddtolist(entry, devices, aliases, options)) { errflag = addtodevicelist(entry); } } /* * Clean up: * - Free the entry space we've allocated. * - Close the device table. * - Build the list to return to the caller. * - Free the accumulate device space (but not the strings!) * - Free the alias list * - Return the built list to the caller. */ returnlist = buildreturnlist(); freedevicelist(); freealiaslist(aliases); _enddevtab(); return (returnlist); } /* * char *oktoaddtolist(devtabentry, devices, aliases, options) * char *devtabentry * char **devices * char **aliases * int options * * This function determines the device "devtabentry" can be * added to the list of devices we're accumulating. If so, * it returns the device name (not the alias). * * Arguments: * devtabentry The device alias that may or may not belong in the * list we're building. * devices The devices specified by the caller * aliases The aliases of the devices specified by the caller * (1-1 correspondence with "devices") * options Options controlling the search */ static char * oktoaddtolist( char *devtabentry, /* Alias to check against list */ char **devices, /* List of devices to check against */ char **aliases, /* List of alias of those devices */ int options) /* Options governing search */ { /* Automatic data */ char *rtnval; /* Value to return */ int found; /* Flag: TRUE if found */ /* If there's a constraint list, is this device in it? */ if (devices && aliases) { /* Set "found" to TRUE if the device is in the list */ found = FALSE; while (!found && *aliases) { if (strcmp(devtabentry, *aliases) == 0) found = TRUE; else { devices++; aliases++; } } /* Set value to return */ if (found) rtnval = (options & DTAB_EXCLUDEFLAG) ? NULL : *devices; else rtnval = (options & DTAB_EXCLUDEFLAG) ? devtabentry : NULL; } else rtnval = devtabentry; /* No constraint list */ return (rtnval); } /* * void initdevicelist() * * This function initializes the list of accumulated devices. * * Arguments: None * * Returns: Void. * * Notes: */ static void initdevicelist(void) { /* Make the list a null list */ (devicelist.head)->next = NULL; devicelist.count = 0; } /* * void freedevicelist() * * This function frees the resources allocated to the linked list of * devices we've been accumulating. * * Arguments: none * * Returns: void */ static void freedevicelist(void) { /* Automatic data */ struct deviceent *pdevice; /* Pointer to current entry */ char *freeblk; /* Pointer space to free */ /* List has a dummy head node */ pdevice = (devicelist.head)->next; while (pdevice) { freeblk = (char *) pdevice; pdevice = pdevice->next; free(freeblk); } } /* * int addtodevicelist(deventry) * char *deventry * * This function adds the device to the list of devices already * accumulated. It will not add the device if that device already exists * in the list. The function returns 0 if successful, -1 if not with * "errno" set (by functions called) to indicate the error. * * Arguments: * deventry char * * The name of the device to add to the list of * accumulated devices * * Returns: * 0 If successful * -1 If failed. "errno" will be set to a value that indicates the * error. * * Notes: * - The memory allocation scheme has the potential to fragment the memory * in the malloc heap. We're allocating space for a local structure, * which will be freed by getdev(), then allocating space for the device * name, which will be freed (maybe) by the application using getdev(). * Not worrying about this at the moment. */ static int addtodevicelist(char *deventry) { /* Automatic data */ struct deviceent *p; /* Pointer to current device */ struct deviceent *q; /* Pointer to next device */ struct deviceent *new; /* Pointer to the alloc'd new node */ char *str; /* Pointer to alloc'd space for name */ int rtncd; /* Value to return to the caller */ int cmpcd; /* strcmp() value, comparing names */ int done; /* Loop control, TRUE if done */ /* Initializations */ rtncd = FALSE; /* * Find the place in the found device list devicelist where this * device is to reside */ p = devicelist.head; done = FALSE; while (!done) { q = p->next; if (!q) done = TRUE; else if ((cmpcd = strcmp(deventry, q->name)) <= 0) done = TRUE; else p = q; } /* * If the device is not already in the list, insert it in the list */ if (!q || (cmpcd != 0)) { /* Alloc space for the new node */ if (new = malloc(sizeof (struct deviceent))) { /* Alloc space for the device character string */ if (str = malloc(strlen(deventry)+1)) { /* * Insert an entry in the found device list containing * this device name */ new->next = q; p->next = new; new->name = strcpy(str, deventry); devicelist.count++; } /* Couldn't alloc space for the device name. Error. */ else rtncd = TRUE; } /* Couldn't alloc space for new node in the found list. Error. */ else rtncd = TRUE; } /* Return an value indicating success or failure */ return (rtncd); } /* * struct srch *buildsearchlist(criteria) * char **criteria * * This function builds a list of search criteria structures from the * criteria strings in the list of criteria whose first argument is * specified by "criteria". * * Arguments: * criteria The address of the first item in a list of * character-strings specifying search criteria * * Returns: struct srch * * The address of the structure in the list of structures describing the * search criteria. * * Notes: * - The only "regular expression" currently supported by the * kywd:exp and kywd!:exp forms is exp=*. This function assumes * that kywd:exp means "if kywd exist" and that kywd!:exp means * "if kywd doesn't exist". */ static struct srch * buildsearchlist(char **criteria) /* Criteria from caller */ { /* Automatic data */ struct srch *rtnbuf; /* Value to return */ struct srch *psrch; /* Running pointer */ char *str; /* Ptr to malloc()ed string space */ char *p; /* Temp pointer to char */ int noerror; /* TRUE if all's well */ int n; /* Temp counter */ char **pp; /* Running ptr to (char *) */ /* Initializations */ rtnbuf = NULL; /* Nothing to return yet */ noerror = TRUE; /* No errors (yet) */ /* If we were given any criteria ... */ if (criteria) { /* Count the number of criteria in the list */ for (n = 1, pp = criteria; *pp++; n++) ; /* Allocate space for structures describing the criteria */ if (rtnbuf = malloc(n*sizeof (struct srch))) { /* Build structures describing the criteria */ pp = criteria; psrch = rtnbuf; while (noerror && *pp) { /* Keep list sane for cleanup if necessary */ psrch->fcn = ENDLIST; /* Alloc space for strings referenced by the structure */ if (str = malloc(strlen(*pp)+1)) { /* Extract field name, function, and compare string */ (void) strcpy(str, *pp); /* If criteria contains an equal sign ('=') ... */ if (p = strchr(str+1, '=')) { if (*(p-1) == '!') { *(p-1) = '\0'; psrch->fcn = NOTEQUAL; } else { *p = '\0'; psrch->fcn = EQUAL; } psrch->cmp = p+1; psrch->name = str; psrch++; } /* If criteria contains a colon (':') ... */ else if (p = strchr(str+1, ':')) { if (*(p-1) == '!') { *(p-1) = '\0'; psrch->fcn = NOEXISTS; } else { *p = '\0'; psrch->fcn = EXISTS; } psrch->cmp = p+1; psrch->name = str; psrch++; } } else { /* Unable to malloc() string space. Clean up */ freesearchlist(rtnbuf); noerror = FALSE; } /* Next criteria */ pp++; } /* Terminate list */ if (noerror) psrch->fcn = ENDLIST; } } /* Return a pointer to allocated space (if any) */ return (rtnbuf); } /* * void freesearchlist(list) * struct srch *list * * This function frees the resources allocated to the searchlist . * * Arguments: * list The list whose resources are to be released. * * Returns: void */ static void freesearchlist(struct srch *list) { /* Automatic data */ struct srch *psrch; /* Running ptr to structs */ /* Free all of the string space allocated for the structure elememts */ for (psrch = list; psrch->fcn != ENDLIST; psrch++) { free(psrch->name); } /* Free the list space */ free(list); } /* * char **buildreturnlist() * * This function builds a list of addresses of character-strings * to be returned from the linked-list of devices we've been * building. It returns a pointer to the first item in that list. * * Arguments: none * * Returns: char ** * The address of the first item in the return list */ static char ** buildreturnlist(void) { /* Automatic data */ char **list; char **q; struct deviceent *p; /* * Allocate space for the return list, * with space for the terminating node */ if (list = malloc((devicelist.count+1)*sizeof (char *))) { /* * Walk the list of accumulated devices, putting pointers to * device names in the list to return */ q = list; for (p = devicelist.head->next; p; p = p->next) *q++ = p->name; /* End the list with a null-pointer */ *q = NULL; } /* Return a pointer to the list we've built */ return (list); } /* * char **makealiaslist(devices) * char **devices List of aliases * * Builds a list of aliases of the devices in the "devices" * list. This list will be terminated by (char *) NULL and * will have the same number of elements as "devices". If * a device couldn't be found, that alias will be "". There * will be a one-to-one correspondence of devices to aliases * in the device list "devices" and the generated list. * * Arguments: * devices The list of devices to derive aliases from * * Returns: char ** * The address of the list of addresses of aliases. The list * and aliases will be allocated using the malloc() function. */ static char ** makealiaslist(char **devices) { /* Automatic data */ char **pp; /* Running ptr to (char *) */ char **qq; /* Running ptr to (char *) */ char **aliases; /* List being returned */ char *alias; /* Alias of current device */ int olderrno; /* Value of errno on entry */ int noerror; /* Flag, TRUE if all's well */ int n; /* Count of entries in "devices" */ noerror = TRUE; olderrno = errno; if (devices) { /* Get the number of entries in the constaint list */ for (n = 1, pp = devices; *pp; pp++) n++; /* Get space for the alias list */ if (aliases = malloc(n*sizeof (char *))) { /* Build the alias list */ qq = aliases; for (pp = devices; noerror && *pp; pp++) { /* Get the device's alias and put it in the list */ if (alias = devattr(*pp, DTAB_ALIAS)) *qq++ = alias; else { errno = olderrno; if (alias = malloc(strlen("")+1)) *qq++ = strcpy(alias, ""); else { /* No space for a null string? Yeech... */ for (qq = aliases; *qq; qq++) free(*qq); free(aliases); aliases = NULL; noerror = FALSE; } } } if (noerror) *qq = NULL; } } else aliases = NULL; /* No constraint list */ /* Return ptr to generated list or NULL if none or error */ return (aliases); } /* * void freealiaslist(aliaslist) * char **aliaslist; * * Free the space allocated to the aliaslist. It frees the space * allocated to the character-strings referenced by the list then * it frees the list. * * Arguments: * aliaslist The address of the first item in the list of * aliases that is to be freed * * Returns: void */ static void freealiaslist(char **aliaslist) /* Ptr to new device list */ { /* Automatic Data */ char **pp; /* Running pointer */ /* If there's a list ... */ if (aliaslist) { /* For each entry in the old list, free the entry */ for (pp = aliaslist; *pp; pp++) free(*pp); /* Free the list */ free(aliaslist); } } /* * char *getnextmatch(criteria, options) * struct srch *criteria * int options * * Gets the next device in the device table that matches the criteria. * Returns the alias of that device. * * Arguments: * criteria The linked list of criteria to use to match a device * options Options modifying the criteria (only one that's really * important is the DTAB_ANDCRITERIA flag) * * Returns: char * * A pointer to a malloc()ed string containing the alias of the next * device that matches the criteria, or (char *) NULL if none. */ static char * getnextmatch(struct srch *criteria, int options) { /* Automatic data */ struct devtabent *devtabent; /* Ptr to current record */ char *alias; /* Alias of device found */ int notdone; /* Flag, done yet? */ int noerror; /* Flag, had an error yet? */ /* * Initializations: * - No alias yet * - Not finished yet * - Make sure there are criteria we're to use */ alias = NULL; notdone = TRUE; noerror = TRUE; /* If we're to "and" the criteria... */ if (options & DTAB_ANDCRITERIA) { /* * Search the device table until we've got a record that matches * all of the criteria or we run out of records */ while (notdone && (devtabent = _getdevtabent())) { if (!devtabent->comment) { if (!criteria || matchallcriteria(devtabent, criteria)) { if (alias = malloc(strlen(devtabent->alias)+1)) (void) strcpy(alias, devtabent->alias); else noerror = FALSE; notdone = FALSE; } } _freedevtabent(devtabent); } } else { /* * Search the device table until we've got a record that matches * any of the criteria or we run out of records */ while (notdone && (devtabent = _getdevtabent())) { if (!devtabent->comment) { if (!criteria || matchanycriteria(devtabent, criteria)) { if (alias = malloc(strlen(devtabent->alias)+1)) (void) strcpy(alias, devtabent->alias); else noerror = FALSE; notdone = FALSE; } } _freedevtabent(devtabent); } } /* Return pointer to extracted alias (or NULL if none) */ if ((alias == NULL) && noerror) errno = ENOENT; return (alias); } /* * int matchallcriteria(devtabent, criteria) * * This function examines the record contained in "devtabent" and * determines if that record meets all of the criteria specified by * "criteria". * * Arguments: * struct devtabent *devtabent The device table entry to examine. * struct srch *criteria The criteria to match. * * Returns: int * Returns TRUE if the record matches criteria, FALSE otherwise. */ static int matchallcriteria( struct devtabent *ent, /* Entry to check */ struct srch *criteria) /* Criteria governing match */ { /* Automatic data */ struct srch *p; /* Pointer to current criteria */ struct attrval *q; /* Pointer to current attr/val pair */ int notfound; /* TRUE if attr found in list */ int failed; /* TRUE if record failed to match */ /* Test only if there's criteria to test against */ if (criteria && (criteria->fcn != ENDLIST)) { failed = FALSE; for (p = criteria; !failed && (p->fcn != ENDLIST); p++) { /* * Don't compare against this criteria if it's function is * "IGNORE" */ if (p->fcn != IGNORE) { if (p->fcn != NOEXISTS) { /* Alias? */ if (strcmp(p->name, DTAB_ALIAS) == 0) failed = !matches(ent->alias, p->cmp, p->fcn); /* Char special device? */ else if (strcmp(p->name, DTAB_CDEVICE) == 0) failed = !matches(ent->cdevice, p->cmp, p->fcn); /* Block special device? */ else if (strcmp(p->name, DTAB_BDEVICE) == 0) failed = !matches(ent->bdevice, p->cmp, p->fcn); /* Pathname? */ else if (strcmp(p->name, DTAB_PATHNAME) == 0) failed = !matches(ent->pathname, p->cmp, p->fcn); /* Check other attributes... */ else { notfound = TRUE; q = ent->attrlist; while (notfound && q) { if (strcmp(p->name, q->attr) == 0) { notfound = FALSE; if (!matches(q->val, p->cmp, p->fcn)) failed = TRUE; } else q = q->next; } if (notfound) failed = TRUE; } } else { if (strcmp(p->name, DTAB_ALIAS) == 0) failed = TRUE; else if (strcmp(p->name, DTAB_CDEVICE) == 0) failed = FALSE; else if (strcmp(p->name, DTAB_BDEVICE) == 0) failed = FALSE; else if (strcmp(p->name, DTAB_PATHNAME) == 0) failed = FALSE; else { q = ent->attrlist; while (!failed && q) { if (strcmp(p->name, q->attr) == 0) failed = TRUE; else q = q->next; } } } } /* Search function is not "IGNORE" */ } /* for loop, checking each criteria */ } /* if (criteria) */ else failed = FALSE; /* No criteria specified, it's a match */ /* Return a value indicating if the record matches all criteria */ return (!failed); } /* * int matchanycriteria(devtabent, criteria) * * This function examines the record contained in "devtabent" and * determines if that record meets any of the criteria specified by * "criteria". * * Arguments: * struct devtabent *devtabent The device table entry to examine. * struct srch *criteria The criteria to match. * * Returns: int * Returns TRUE if the record matches criteria, FALSE otherwise. */ static int matchanycriteria( struct devtabent *ent, /* Entry to check */ struct srch *criteria) /* Criteria governing match */ { /* Automatic data */ struct srch *p; /* Pointer to current criteria */ struct attrval *q; /* Pointer to current attr/val pair */ int matched; /* FLAG: TRUE if record matched */ int found; /* FLAG: TRUE if attribute found */ /* Test only if there's criteria to test against */ if (criteria && (criteria->fcn != ENDLIST)) { matched = FALSE; for (p = criteria; !matched && (p->fcn != ENDLIST); p++) { /* * Don't compare against this criteria if it's function is * "IGNORE" */ if (p->fcn != IGNORE) { if (p->fcn != NOEXISTS) { /* Alias? */ if (strcmp(p->name, DTAB_ALIAS) == 0) matched = matches(ent->alias, p->cmp, p->fcn); /* Char special device? */ else if (strcmp(p->name, DTAB_CDEVICE) == 0) matched = matches(ent->cdevice, p->cmp, p->fcn); /* Block special device? */ else if (strcmp(p->name, DTAB_BDEVICE) == 0) matched = matches(ent->bdevice, p->cmp, p->fcn); /* Pathname? */ else if (strcmp(p->name, DTAB_PATHNAME) == 0) matched = matches(ent->pathname, p->cmp, p->fcn); /* Check other attributes... */ else { q = ent->attrlist; found = FALSE; while (!found && q) if (strcmp(p->name, q->attr) == 0) { matched = matches(q->val, p->cmp, p->fcn); found = TRUE; } else q = q->next; } } else { if (strcmp(p->name, DTAB_ALIAS) == 0) matched = FALSE; else if (strcmp(p->name, DTAB_CDEVICE) == 0) matched = FALSE; else if (strcmp(p->name, DTAB_BDEVICE) == 0) matched = FALSE; else if (strcmp(p->name, DTAB_PATHNAME) == 0) matched = FALSE; else { q = ent->attrlist; matched = TRUE; while (matched && q) { if (strcmp(p->name, q->attr) == 0) matched = FALSE; else q = q->next; } } } } /* Search function is not "IGNORE" */ } /* for loop, checking each criteria */ } /* if (criteria) */ else matched = TRUE; /* No criteria specified, it's a match */ /* Return a value indicating if the record matches all criteria */ return (matched); } /* * int matches(value, compare, function) * char *value * char *compare * int function * * This function sees if the operation is satisfied by * comparing the value with . It returns TRUE * if so, FALSE otherwise. * * Arguments: * value Value to compare * compare Value to compare against * function Function to be satisfied * * Returns: int * TRUE if the function is satisfied, FALSE otherwise */ static int matches(char *value, char *compare, int function) { /* Automatic data */ int rtn; /* Value to return */ if (value == NULL) value = ""; /* Do case depending on the function */ switch (function) { /* attr=val */ case EQUAL: rtn = (strcmp(value, compare) == 0); break; /* attr!=val */ case NOTEQUAL: rtn = (strcmp(value, compare) != 0); break; /* attr:* */ case EXISTS: rtn = TRUE; break; /* attr!:* */ case NOEXISTS: rtn = FALSE; break; /* Shouldn't get here... */ default: rtn = FALSE; break; } /* Return a value indicating if the match was made */ return (rtn); }