2N/A/*
2N/A * CDDL HEADER START
2N/A *
2N/A * The contents of this file are subject to the terms of the
2N/A * Common Development and Distribution License, Version 1.0 only
2N/A * (the "License"). You may not use this file except in compliance
2N/A * with the License.
2N/A *
2N/A * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
2N/A * or http://www.opensolaris.org/os/licensing.
2N/A * See the License for the specific language governing permissions
2N/A * and limitations under the License.
2N/A *
2N/A * When distributing Covered Code, include this CDDL HEADER in each
2N/A * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
2N/A * If applicable, add the following below this CDDL HEADER, with the
2N/A * fields enclosed by brackets "[]" replaced with your own identifying
2N/A * information: Portions Copyright [yyyy] [name of copyright owner]
2N/A *
2N/A * CDDL HEADER END
2N/A */
2N/A/*
2N/A * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
2N/A * Use is subject to license terms.
2N/A */
2N/A
2N/A#pragma ident "%Z%%M% %I% %E% SMI"
2N/A
2N/A#include "cfga_usb.h"
2N/A
2N/A
2N/A#define MAXLINESIZE 512
2N/A#define FE_BUFLEN 256
2N/A
2N/A#define isunary(ch) ((ch) == '~' || (ch) == '-')
2N/A#define iswhite(ch) ((ch) == ' ' || (ch) == '\t')
2N/A#define isnewline(ch) ((ch) == '\n' || (ch) == '\r' || (ch) == '\f')
2N/A#define isalphanum(ch) (isalpha(ch) || isdigit(ch))
2N/A#define isnamechar(ch) (isalphanum(ch) || (ch) == '_' || (ch) == '-')
2N/A
2N/A#define MAX(a, b) ((a) < (b) ? (b) : (a))
2N/A#define GETC(a, cntr) a[cntr++]
2N/A#define UNGETC(cntr) cntr--
2N/A
2N/A
2N/Atypedef struct usb_configrec {
2N/A char *selection;
2N/A int idVendor, idProduct, cfgndx;
2N/A char *serialno;
2N/A char *pathname;
2N/A char *driver;
2N/A} usb_configrec_t;
2N/A
2N/Atypedef enum {
2N/A USB_SELECTION, USB_VENDOR, USB_PRODUCT, USB_CFGNDX, USB_SRNO,
2N/A USB_PATH, USB_DRIVER, USB_NONE
2N/A} config_field_t;
2N/A
2N/Atypedef struct usbcfg_var {
2N/A const char *name;
2N/A config_field_t field;
2N/A} usbcfg_var_t;
2N/A
2N/Astatic usbcfg_var_t usbcfg_varlist[] = {
2N/A { "selection", USB_SELECTION },
2N/A { "idVendor", USB_VENDOR },
2N/A { "idProduct", USB_PRODUCT },
2N/A { "cfgndx", USB_CFGNDX },
2N/A { "srno", USB_SRNO },
2N/A { "pathname", USB_PATH },
2N/A { "driver", USB_DRIVER },
2N/A { NULL, USB_NONE }
2N/A};
2N/A
2N/Atypedef enum {
2N/A EQUALS,
2N/A AMPERSAND,
2N/A BIT_OR,
2N/A STAR,
2N/A POUND,
2N/A COLON,
2N/A SEMICOLON,
2N/A COMMA,
2N/A SLASH,
2N/A WHITE_SPACE,
2N/A NEWLINE,
2N/A E_O_F,
2N/A STRING,
2N/A HEXVAL,
2N/A DECVAL,
2N/A NAME
2N/A} token_t;
2N/A
2N/A
2N/Astatic char usbconf_file[] = USBCONF_FILE;
2N/Astatic int linenum = 1;
2N/Astatic int cntr = 0;
2N/Astatic int frec = 0;
2N/Astatic int brec = 0;
2N/Astatic int btoken = 0;
2N/Amutex_t file_lock = DEFAULTMUTEX;
2N/A
2N/A
2N/A/*
2N/A * prototypes
2N/A */
2N/Astatic int get_string(u_longlong_t *llptr, char *tchar);
2N/Astatic int getvalue(char *token, u_longlong_t *valuep);
2N/A
2N/A
2N/A/*
2N/A * The next item on the line is a string value. Allocate memory for
2N/A * it and copy the string. Return 1, and set arg ptr to newly allocated
2N/A * and initialized buffer, or NULL if an error occurs.
2N/A */
2N/Astatic int
2N/Aget_string(u_longlong_t *llptr, char *tchar)
2N/A{
2N/A register char *cp;
2N/A register char *start = (char *)0;
2N/A register int len = 0;
2N/A
2N/A len = strlen(tchar);
2N/A start = tchar;
2N/A /* copy string */
2N/A cp = (char *)calloc(len + 1, sizeof (char));
2N/A if (cp == (char *)NULL) {
2N/A *llptr = NULL;
2N/A
2N/A return (0);
2N/A }
2N/A
2N/A *llptr = (u_longlong_t)(uintptr_t)cp;
2N/A for (; len > 0; len--) {
2N/A /* convert some common escape sequences */
2N/A if (*start == '\\') {
2N/A switch (*(start + 1)) {
2N/A case 't':
2N/A /* tab */
2N/A *cp++ = '\t';
2N/A len--;
2N/A start += 2;
2N/A break;
2N/A case 'n':
2N/A /* new line */
2N/A *cp++ = '\n';
2N/A len--;
2N/A start += 2;
2N/A break;
2N/A case 'b':
2N/A /* back space */
2N/A *cp++ = '\b';
2N/A len--;
2N/A start += 2;
2N/A break;
2N/A default:
2N/A /* simply copy it */
2N/A *cp++ = *start++;
2N/A break;
2N/A }
2N/A } else {
2N/A *cp++ = *start++;
2N/A }
2N/A }
2N/A *cp = '\0';
2N/A return (1);
2N/A}
2N/A
2N/A
2N/A/*
2N/A * get a decimal octal or hex number. Handle '~' for one's complement.
2N/A */
2N/Astatic int
2N/Agetvalue(char *token, u_longlong_t *valuep)
2N/A{
2N/A register int radix;
2N/A register u_longlong_t retval = 0;
2N/A register int onescompl = 0;
2N/A register int negate = 0;
2N/A register char c;
2N/A
2N/A if (*token == '~') {
2N/A onescompl++; /* perform one's complement on result */
2N/A token++;
2N/A } else if (*token == '-') {
2N/A negate++;
2N/A token++;
2N/A }
2N/A if (*token == '0') {
2N/A token++;
2N/A c = *token;
2N/A
2N/A if (c == '\0') {
2N/A *valuep = 0; /* value is 0 */
2N/A return (0);
2N/A }
2N/A
2N/A if (c == 'x' || c == 'X') {
2N/A radix = 16;
2N/A token++;
2N/A } else {
2N/A radix = 8;
2N/A }
2N/A } else {
2N/A radix = 10;
2N/A }
2N/A
2N/A while ((c = *token++)) {
2N/A switch (radix) {
2N/A case 8:
2N/A if (c >= '0' && c <= '7') {
2N/A c -= '0';
2N/A } else {
2N/A return (-1); /* invalid number */
2N/A }
2N/A retval = (retval << 3) + c;
2N/A break;
2N/A case 10:
2N/A if (c >= '0' && c <= '9') {
2N/A c -= '0';
2N/A } else {
2N/A return (-1); /* invalid number */
2N/A }
2N/A retval = (retval * 10) + c;
2N/A break;
2N/A case 16:
2N/A if (c >= 'a' && c <= 'f') {
2N/A c = c - 'a' + 10;
2N/A } else if (c >= 'A' && c <= 'F') {
2N/A c = c - 'A' + 10;
2N/A } else if (c >= '0' && c <= '9') {
2N/A c -= '0';
2N/A } else {
2N/A return (-1); /* invalid number */
2N/A }
2N/A retval = (retval << 4) + c;
2N/A break;
2N/A }
2N/A }
2N/A if (onescompl)
2N/A retval = ~retval;
2N/A if (negate)
2N/A retval = -retval;
2N/A *valuep = retval;
2N/A
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * returns the field from the token
2N/A */
2N/Astatic config_field_t
2N/Ausb_get_var_type(char *str)
2N/A{
2N/A usbcfg_var_t *cfgvar;
2N/A
2N/A cfgvar = &usbcfg_varlist[0];
2N/A while (cfgvar->field != USB_NONE) {
2N/A if (strcasecmp(cfgvar->name, str) == NULL) {
2N/A break;
2N/A } else {
2N/A cfgvar++;
2N/A }
2N/A }
2N/A
2N/A return (cfgvar->field);
2N/A}
2N/A
2N/A
2N/A/* ARGSUSED */
2N/Astatic token_t
2N/Alex(char *buf, char *val, char **errmsg)
2N/A{
2N/A int ch, oval, badquote;
2N/A char *cp;
2N/A token_t token;
2N/A
2N/A cp = val;
2N/A while ((ch = GETC(buf, cntr)) == ' ' || ch == '\t');
2N/A
2N/A /*
2N/A * Note the beginning of a token
2N/A */
2N/A btoken = cntr - 1;
2N/A
2N/A *cp++ = (char)ch;
2N/A switch (ch) {
2N/A case '=':
2N/A token = EQUALS;
2N/A break;
2N/A case '&':
2N/A token = AMPERSAND;
2N/A break;
2N/A case '|':
2N/A token = BIT_OR;
2N/A break;
2N/A case '*':
2N/A token = STAR;
2N/A break;
2N/A case '#':
2N/A token = POUND;
2N/A break;
2N/A case ':':
2N/A token = COLON;
2N/A break;
2N/A case ';':
2N/A token = SEMICOLON;
2N/A break;
2N/A case ',':
2N/A token = COMMA;
2N/A break;
2N/A case '/':
2N/A token = SLASH;
2N/A break;
2N/A case ' ':
2N/A case '\t':
2N/A case '\f':
2N/A while ((ch = GETC(buf, cntr)) == ' ' ||
2N/A ch == '\t' || ch == '\f')
2N/A *cp++ = (char)ch;
2N/A (void) UNGETC(cntr);
2N/A token = WHITE_SPACE;
2N/A break;
2N/A case '\n':
2N/A case '\r':
2N/A token = NEWLINE;
2N/A break;
2N/A case '"':
2N/A cp--;
2N/A badquote = 0;
2N/A while (!badquote && (ch = GETC(buf, cntr)) != '"') {
2N/A switch (ch) {
2N/A case '\n':
2N/A case -1:
2N/A (void) snprintf(*errmsg, MAXPATHLEN,
2N/A "Missing \"");
2N/A cp = val;
2N/A *cp++ = '\n';
2N/A badquote = 1;
2N/A /* since we consumed the newline/EOF */
2N/A (void) UNGETC(cntr);
2N/A break;
2N/A
2N/A case '\\':
2N/A ch = (char)GETC(buf, cntr);
2N/A if (!isdigit(ch)) {
2N/A /* escape the character */
2N/A *cp++ = (char)ch;
2N/A break;
2N/A }
2N/A oval = 0;
2N/A while (ch >= '0' && ch <= '7') {
2N/A ch -= '0';
2N/A oval = (oval << 3) + ch;
2N/A ch = (char)GETC(buf, cntr);
2N/A }
2N/A (void) UNGETC(cntr);
2N/A /* check for character overflow? */
2N/A if (oval > 127) {
2N/A (void) snprintf(*errmsg, MAXPATHLEN,
2N/A "Character overflow detected.\n");
2N/A }
2N/A *cp++ = (char)oval;
2N/A break;
2N/A default:
2N/A *cp++ = (char)ch;
2N/A break;
2N/A }
2N/A }
2N/A token = STRING;
2N/A break;
2N/A
2N/A default:
2N/A if (ch == -1) {
2N/A token = EOF;
2N/A break;
2N/A }
2N/A /*
2N/A * detect a lone '-' (including at the end of a line), and
2N/A * identify it as a 'name'
2N/A */
2N/A if (ch == '-') {
2N/A *cp++ = (char)(ch = GETC(buf, cntr));
2N/A if (iswhite(ch) || (ch == '\n')) {
2N/A (void) UNGETC(cntr);
2N/A cp--;
2N/A token = NAME;
2N/A break;
2N/A }
2N/A } else if (isunary(ch)) {
2N/A *cp++ = (char)(ch = GETC(buf, cntr));
2N/A }
2N/A
2N/A if (isdigit(ch)) {
2N/A if (ch == '0') {
2N/A if ((ch = GETC(buf, cntr)) == 'x') {
2N/A *cp++ = (char)ch;
2N/A ch = GETC(buf, cntr);
2N/A while (isxdigit(ch)) {
2N/A *cp++ = (char)ch;
2N/A ch = GETC(buf, cntr);
2N/A }
2N/A (void) UNGETC(cntr);
2N/A token = HEXVAL;
2N/A } else {
2N/A goto digit;
2N/A }
2N/A } else {
2N/A ch = GETC(buf, cntr);
2N/Adigit:
2N/A while (isdigit(ch)) {
2N/A *cp++ = (char)ch;
2N/A ch = GETC(buf, cntr);
2N/A }
2N/A (void) UNGETC(cntr);
2N/A token = DECVAL;
2N/A }
2N/A } else if (isalpha(ch) || ch == '\\') {
2N/A if (ch != '\\') {
2N/A ch = GETC(buf, cntr);
2N/A } else {
2N/A /*
2N/A * if the character was a backslash,
2N/A * back up so we can overwrite it with
2N/A * the next (i.e. escaped) character.
2N/A */
2N/A cp--;
2N/A }
2N/A
2N/A while (isnamechar(ch) || ch == '\\') {
2N/A if (ch == '\\')
2N/A ch = GETC(buf, cntr);
2N/A *cp++ = (char)ch;
2N/A ch = GETC(buf, cntr);
2N/A }
2N/A (void) UNGETC(cntr);
2N/A token = NAME;
2N/A } else {
2N/A
2N/A return (-1);
2N/A }
2N/A break;
2N/A }
2N/A *cp = '\0';
2N/A
2N/A return (token);
2N/A}
2N/A
2N/A
2N/A/*
2N/A * Leave NEWLINE as the next character.
2N/A */
2N/Astatic void
2N/Afind_eol(char *buf)
2N/A{
2N/A register int ch;
2N/A
2N/A while ((ch = GETC(buf, cntr)) != -1) {
2N/A if (isnewline(ch)) {
2N/A (void) UNGETC(cntr);
2N/A break;
2N/A }
2N/A }
2N/A}
2N/A
2N/A
2N/A/*
2N/A * Fetch one record from the USBCONF_FILE
2N/A */
2N/Astatic token_t
2N/Ausb_get_conf_rec(char *buf, usb_configrec_t **rec, char **errmsg)
2N/A{
2N/A token_t token;
2N/A char tokval[MAXLINESIZE];
2N/A usb_configrec_t *user_rec;
2N/A config_field_t cfgvar;
2N/A u_longlong_t llptr;
2N/A u_longlong_t value;
2N/A boolean_t sor = B_TRUE;
2N/A
2N/A enum {
2N/A USB_NEWVAR, USB_CONFIG_VAR, USB_VAR_EQUAL, USB_VAR_VALUE,
2N/A USB_ERROR
2N/A } parse_state = USB_NEWVAR;
2N/A
2N/A DPRINTF("usb_get_conf_rec:\n");
2N/A
2N/A user_rec = (usb_configrec_t *)calloc(1, sizeof (usb_configrec_t));
2N/A if (user_rec == (usb_configrec_t *)NULL) {
2N/A return (0);
2N/A }
2N/A
2N/A user_rec->idVendor = user_rec->idProduct = user_rec->cfgndx = -1;
2N/A
2N/A token = lex(buf, tokval, errmsg);
2N/A while ((token != EOF) && (token != SEMICOLON)) {
2N/A switch (token) {
2N/A case STAR:
2N/A case POUND:
2N/A /* skip comments */
2N/A find_eol(buf);
2N/A break;
2N/A case NEWLINE:
2N/A linenum++;
2N/A break;
2N/A case NAME:
2N/A case STRING:
2N/A switch (parse_state) {
2N/A case USB_NEWVAR:
2N/A cfgvar = usb_get_var_type(tokval);
2N/A if (cfgvar == USB_NONE) {
2N/A parse_state = USB_ERROR;
2N/A (void) snprintf(*errmsg, MAXPATHLEN,
2N/A "Syntax Error: Invalid field %s",
2N/A tokval);
2N/A } else {
2N/A /*
2N/A * Note the beginning of a record
2N/A */
2N/A if (sor) {
2N/A brec = btoken;
2N/A if (frec == 0) frec = brec;
2N/A sor = B_FALSE;
2N/A }
2N/A parse_state = USB_CONFIG_VAR;
2N/A }
2N/A break;
2N/A case USB_VAR_VALUE:
2N/A if ((cfgvar == USB_VENDOR) ||
2N/A (cfgvar == USB_PRODUCT) ||
2N/A (cfgvar == USB_CFGNDX)) {
2N/A parse_state = USB_ERROR;
2N/A (void) snprintf(*errmsg, MAXPATHLEN,
2N/A "Syntax Error: Invalid value %s "
2N/A "for field: %s\n", tokval,
2N/A usbcfg_varlist[cfgvar].name);
2N/A } else if (get_string(&llptr, tokval)) {
2N/A switch (cfgvar) {
2N/A case USB_SELECTION:
2N/A user_rec->selection =
2N/A (char *)(uintptr_t)llptr;
2N/A parse_state = USB_NEWVAR;
2N/A break;
2N/A case USB_SRNO:
2N/A user_rec->serialno =
2N/A (char *)(uintptr_t)llptr;
2N/A parse_state = USB_NEWVAR;
2N/A break;
2N/A case USB_PATH:
2N/A user_rec->pathname =
2N/A (char *)(uintptr_t)llptr;
2N/A parse_state = USB_NEWVAR;
2N/A break;
2N/A case USB_DRIVER:
2N/A user_rec->driver =
2N/A (char *)(uintptr_t)llptr;
2N/A parse_state = USB_NEWVAR;
2N/A break;
2N/A default:
2N/A parse_state = USB_ERROR;
2N/A free((void *)(uintptr_t)llptr);
2N/A }
2N/A } else {
2N/A parse_state = USB_ERROR;
2N/A (void) snprintf(*errmsg, MAXPATHLEN,
2N/A "Syntax Error: Invalid value %s "
2N/A "for field: %s\n", tokval,
2N/A usbcfg_varlist[cfgvar].name);
2N/A }
2N/A break;
2N/A case USB_ERROR:
2N/A /* just skip */
2N/A break;
2N/A default:
2N/A parse_state = USB_ERROR;
2N/A (void) snprintf(*errmsg, MAXPATHLEN,
2N/A "Syntax Error: at %s", tokval);
2N/A break;
2N/A }
2N/A break;
2N/A case EQUALS:
2N/A if (parse_state == USB_CONFIG_VAR) {
2N/A if (cfgvar == USB_NONE) {
2N/A parse_state = USB_ERROR;
2N/A (void) snprintf(*errmsg, MAXPATHLEN,
2N/A "Syntax Error: unexpected '='");
2N/A } else {
2N/A parse_state = USB_VAR_VALUE;
2N/A }
2N/A } else if (parse_state != USB_ERROR) {
2N/A (void) snprintf(*errmsg, MAXPATHLEN,
2N/A "Syntax Error: unexpected '='");
2N/A parse_state = USB_ERROR;
2N/A }
2N/A break;
2N/A case HEXVAL:
2N/A case DECVAL:
2N/A if ((parse_state == USB_VAR_VALUE) && (cfgvar !=
2N/A USB_NONE)) {
2N/A (void) getvalue(tokval, &value);
2N/A switch (cfgvar) {
2N/A case USB_VENDOR:
2N/A user_rec->idVendor = (int)value;
2N/A parse_state = USB_NEWVAR;
2N/A break;
2N/A case USB_PRODUCT:
2N/A user_rec->idProduct = (int)value;
2N/A parse_state = USB_NEWVAR;
2N/A break;
2N/A case USB_CFGNDX:
2N/A user_rec->cfgndx = (int)value;
2N/A parse_state = USB_NEWVAR;
2N/A break;
2N/A default:
2N/A (void) snprintf(*errmsg, MAXPATHLEN,
2N/A "Syntax Error: Invalid value for "
2N/A "%s", usbcfg_varlist[cfgvar].name);
2N/A }
2N/A } else if (parse_state != USB_ERROR) {
2N/A parse_state = USB_ERROR;
2N/A (void) snprintf(*errmsg, MAXPATHLEN,
2N/A "Syntax Error: unexpected hex/decimal: %s",
2N/A tokval);
2N/A }
2N/A break;
2N/A default:
2N/A (void) snprintf(*errmsg, MAXPATHLEN,
2N/A "Syntax Error: at: %s", tokval);
2N/A parse_state = USB_ERROR;
2N/A break;
2N/A }
2N/A token = lex(buf, tokval, errmsg);
2N/A }
2N/A *rec = user_rec;
2N/A
2N/A return (token);
2N/A}
2N/A
2N/A
2N/A/*
2N/A * Here we compare the two records and determine if they are the same
2N/A */
2N/Astatic boolean_t
2N/Ausb_cmp_rec(usb_configrec_t *cfg_rec, usb_configrec_t *user_rec)
2N/A{
2N/A char *ustr, *cstr;
2N/A boolean_t srno = B_FALSE, path = B_FALSE;
2N/A
2N/A DPRINTF("usb_cmp_rec:\n");
2N/A
2N/A if ((cfg_rec->idVendor == user_rec->idVendor) &&
2N/A (cfg_rec->idProduct == user_rec->idProduct)) {
2N/A if (user_rec->serialno) {
2N/A if (cfg_rec->serialno) {
2N/A srno = (strcmp(cfg_rec->serialno,
2N/A user_rec->serialno) == 0);
2N/A } else {
2N/A
2N/A return (B_FALSE);
2N/A }
2N/A
2N/A } else if (user_rec->pathname) {
2N/A if (cfg_rec->pathname) {
2N/A /*
2N/A * Comparing on this is tricky. At this point
2N/A * hubd knows: ../hubd@P/device@P while user
2N/A * will specify ..../hubd@P/keyboard@P
2N/A * First compare till .../hubd@P
2N/A * Second compare is just P in "device@P"
2N/A *
2N/A * XXX: note that we assume P as one character
2N/A * as there are no 2 digit hubs in the market.
2N/A */
2N/A ustr = strrchr(user_rec->pathname, '/');
2N/A cstr = strrchr(cfg_rec->pathname, '/');
2N/A path = (strncmp(cfg_rec->pathname,
2N/A user_rec->pathname,
2N/A MAX(ustr - user_rec->pathname,
2N/A cstr - cfg_rec->pathname)) == 0);
2N/A path = path && (*(user_rec->pathname +
2N/A strlen(user_rec->pathname) -1) ==
2N/A *(cfg_rec->pathname +
2N/A strlen(cfg_rec->pathname) - 1));
2N/A } else {
2N/A
2N/A return (B_FALSE);
2N/A }
2N/A
2N/A } else if (cfg_rec->serialno || cfg_rec->pathname) {
2N/A
2N/A return (B_FALSE);
2N/A } else {
2N/A
2N/A return (B_TRUE);
2N/A }
2N/A
2N/A return (srno || path);
2N/A } else {
2N/A
2N/A return (B_FALSE);
2N/A }
2N/A}
2N/A
2N/A
2N/A/*
2N/A * free the record allocated in usb_get_conf_rec
2N/A */
2N/Astatic void
2N/Ausb_free_rec(usb_configrec_t *rec)
2N/A{
2N/A if (rec == (usb_configrec_t *)NULL) {
2N/A
2N/A return;
2N/A }
2N/A
2N/A free(rec->selection);
2N/A free(rec->serialno);
2N/A free(rec->pathname);
2N/A free(rec->driver);
2N/A free(rec);
2N/A}
2N/A
2N/A
2N/Aint
2N/Aadd_entry(char *selection, int vid, int pid, int cfgndx, char *srno,
2N/A char *path, char *driver, char **errmsg)
2N/A{
2N/A int file;
2N/A int rval = CFGA_USB_OK;
2N/A char *buf = (char *)NULL;
2N/A char str[MAXLINESIZE];
2N/A token_t token = NEWLINE;
2N/A boolean_t found = B_FALSE;
2N/A struct stat st;
2N/A usb_configrec_t cfgrec, *user_rec = NULL;
2N/A
2N/A DPRINTF("add_entry: driver=%s, path=%s\n",
2N/A driver ? driver : "", path ? path : "");
2N/A
2N/A if (*errmsg == (char *)NULL) {
2N/A if ((*errmsg = calloc(MAXPATHLEN, 1)) == (char *)NULL) {
2N/A
2N/A return (CFGA_USB_CONFIG_FILE);
2N/A }
2N/A }
2N/A
2N/A (void) mutex_lock(&file_lock);
2N/A
2N/A /* Initialize the cfgrec */
2N/A cfgrec.selection = selection;
2N/A cfgrec.idVendor = vid;
2N/A cfgrec.idProduct = pid;
2N/A cfgrec.cfgndx = cfgndx;
2N/A cfgrec.serialno = srno;
2N/A cfgrec.pathname = path;
2N/A cfgrec.driver = driver;
2N/A
2N/A /* open config_map.conf file */
2N/A file = open(usbconf_file, O_RDWR, 0666);
2N/A if (file == -1) {
2N/A (void) snprintf(*errmsg, MAXPATHLEN,
2N/A "failed to open config file\n");
2N/A (void) mutex_unlock(&file_lock);
2N/A
2N/A return (CFGA_USB_CONFIG_FILE);
2N/A }
2N/A
2N/A if (lockf(file, F_TLOCK, 0) == -1) {
2N/A (void) snprintf(*errmsg, MAXPATHLEN,
2N/A "failed to lock config file\n");
2N/A close(file);
2N/A (void) mutex_unlock(&file_lock);
2N/A
2N/A return (CFGA_USB_LOCK_FILE);
2N/A }
2N/A
2N/A /*
2N/A * These variables need to be reinitialized here as they may
2N/A * have been modified by a previous thread that called this
2N/A * function
2N/A */
2N/A linenum = 1;
2N/A cntr = 0;
2N/A frec = 0;
2N/A brec = 0;
2N/A btoken = 0;
2N/A
2N/A if (fstat(file, &st) != 0) {
2N/A DPRINTF("add_entry: failed to fstat config file\n");
2N/A rval = CFGA_USB_CONFIG_FILE;
2N/A goto exit;
2N/A }
2N/A
2N/A if ((buf = (char *)malloc(st.st_size)) == NULL) {
2N/A DPRINTF("add_entry: failed to fstat config file\n");
2N/A rval = CFGA_USB_ALLOC_FAIL;
2N/A goto exit;
2N/A }
2N/A
2N/A if (st.st_size != read(file, buf, st.st_size)) {
2N/A DPRINTF("add_entry: failed to read config file\n");
2N/A rval = CFGA_USB_CONFIG_FILE;
2N/A goto exit;
2N/A }
2N/A
2N/A /* set up for reading the file */
2N/A
2N/A while ((token != EOF) && !found) {
2N/A if (user_rec) {
2N/A usb_free_rec(user_rec);
2N/A user_rec = NULL;
2N/A }
2N/A token = usb_get_conf_rec(buf, &user_rec, errmsg);
2N/A found = usb_cmp_rec(&cfgrec, user_rec);
2N/A DPRINTF("add_entry: token=%x, found=%x\n", token, found);
2N/A }
2N/A
2N/A bzero(str, MAXLINESIZE);
2N/A
2N/A if (found) {
2N/A DPRINTF("FOUND\n");
2N/A (void) snprintf(str, MAXLINESIZE, "selection=%s idVendor=0x%x "
2N/A "idProduct=0x%x ",
2N/A (cfgrec.selection) ? cfgrec.selection : user_rec->selection,
2N/A user_rec->idVendor, user_rec->idProduct);
2N/A
2N/A if ((user_rec->cfgndx != -1) || (cfgrec.cfgndx != -1)) {
2N/A (void) snprintf(&str[strlen(str)], MAXLINESIZE,
2N/A "cfgndx=0x%x ", (cfgrec.cfgndx != -1) ?
2N/A cfgrec.cfgndx : user_rec->cfgndx);
2N/A }
2N/A
2N/A if (user_rec->serialno) {
2N/A (void) snprintf(&str[strlen(str)], MAXLINESIZE,
2N/A "srno=\"%s\" ", user_rec->serialno);
2N/A }
2N/A
2N/A if (user_rec->pathname) {
2N/A (void) snprintf(&str[strlen(str)], MAXLINESIZE,
2N/A "pathname=\"%s\" ", user_rec->pathname);
2N/A }
2N/A
2N/A if (user_rec->driver) {
2N/A (void) snprintf(&str[strlen(str)], MAXLINESIZE,
2N/A "driver=\"%s\" ", user_rec->driver);
2N/A } else if (cfgrec.driver != NULL) {
2N/A if (strlen(cfgrec.driver)) {
2N/A (void) snprintf(&str[strlen(str)], MAXLINESIZE,
2N/A "driver=\"%s\" ", cfgrec.driver);
2N/A }
2N/A }
2N/A
2N/A (void) strlcat(str, ";", sizeof (str));
2N/A
2N/A /*
2N/A * Seek to the beginning of the record
2N/A */
2N/A if (lseek(file, brec, SEEK_SET) == -1) {
2N/A DPRINTF("add_entry: failed to lseek config file\n");
2N/A rval = CFGA_USB_CONFIG_FILE;
2N/A goto exit;
2N/A }
2N/A
2N/A /*
2N/A * Write the modified record
2N/A */
2N/A if (write(file, str, strlen(str)) == -1) {
2N/A DPRINTF("add_entry: failed to write config file\n");
2N/A rval = CFGA_USB_CONFIG_FILE;
2N/A goto exit;
2N/A }
2N/A
2N/A /*
2N/A * Write the rest of the file as it was
2N/A */
2N/A if (write(file, buf+cntr, st.st_size - cntr) == -1) {
2N/A DPRINTF("add_entry: failed to write config file\n");
2N/A rval = CFGA_USB_CONFIG_FILE;
2N/A goto exit;
2N/A }
2N/A
2N/A } else {
2N/A DPRINTF("!FOUND\n");
2N/A (void) snprintf(str, MAXLINESIZE,
2N/A "selection=%s idVendor=0x%x idProduct=0x%x ",
2N/A (cfgrec.selection) ? cfgrec.selection : "enable",
2N/A cfgrec.idVendor, cfgrec.idProduct);
2N/A
2N/A if (cfgrec.cfgndx != -1) {
2N/A (void) snprintf(&str[strlen(str)], MAXLINESIZE,
2N/A "cfgndx=0x%x ", cfgrec.cfgndx);
2N/A }
2N/A
2N/A if (cfgrec.serialno) {
2N/A (void) snprintf(&str[strlen(str)], MAXLINESIZE,
2N/A "srno=\"%s\" ", cfgrec.serialno);
2N/A }
2N/A
2N/A if (cfgrec.pathname != NULL) {
2N/A (void) snprintf(&str[strlen(str)], MAXLINESIZE,
2N/A "pathname=\"%s\" ", cfgrec.pathname);
2N/A }
2N/A
2N/A if (cfgrec.driver != NULL) {
2N/A if (strlen(cfgrec.driver)) {
2N/A (void) snprintf(&str[strlen(str)], MAXLINESIZE,
2N/A "driver=\"%s\" ", cfgrec.driver);
2N/A }
2N/A }
2N/A
2N/A (void) strlcat(str, ";\n", sizeof (str));
2N/A
2N/A /*
2N/A * Incase this is the first entry, add it after the comments
2N/A */
2N/A if (frec == 0) {
2N/A frec = st.st_size;
2N/A }
2N/A
2N/A /*
2N/A * Go to the beginning of the records
2N/A */
2N/A if (lseek(file, frec, SEEK_SET) == -1) {
2N/A DPRINTF("add_entry: failed to lseek config file\n");
2N/A rval = CFGA_USB_CONFIG_FILE;
2N/A goto exit;
2N/A }
2N/A
2N/A /*
2N/A * Add the entry
2N/A */
2N/A if (write(file, str, strlen(str)) == -1) {
2N/A DPRINTF("add_entry: failed to write config file\n");
2N/A rval = CFGA_USB_CONFIG_FILE;
2N/A goto exit;
2N/A }
2N/A
2N/A /*
2N/A * write the remaining file as it was
2N/A */
2N/A if (write(file, buf+frec, st.st_size - frec) == -1) {
2N/A DPRINTF("add_entry: failed to write config file\n");
2N/A rval = CFGA_USB_CONFIG_FILE;
2N/A goto exit;
2N/A }
2N/A }
2N/A
2N/A /* no error encountered */
2N/A if (rval == CFGA_USB_OK) {
2N/A free(errmsg);
2N/A }
2N/A
2N/Aexit:
2N/A if (buf != NULL) {
2N/A free(buf);
2N/A }
2N/A
2N/A if (lockf(file, F_ULOCK, 0) == -1) {
2N/A DPRINTF("add_entry: failed to unlock config file\n");
2N/A
2N/A rval = CFGA_USB_LOCK_FILE;
2N/A }
2N/A
2N/A close(file);
2N/A
2N/A (void) mutex_unlock(&file_lock);
2N/A
2N/A return (rval);
2N/A}