1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #include "cfga_usb.h" 28 29 30 #define MAXLINESIZE 512 31 #define FE_BUFLEN 256 32 33 #define isunary(ch) ((ch) == '~' || (ch) == '-') 34 #define iswhite(ch) ((ch) == ' ' || (ch) == '\t') 35 #define isnewline(ch) ((ch) == '\n' || (ch) == '\r' || (ch) == '\f') 36 #define isalphanum(ch) (isalpha(ch) || isdigit(ch)) 37 #define isnamechar(ch) (isalphanum(ch) || (ch) == '_' || (ch) == '-') 38 39 #define MAX(a, b) ((a) < (b) ? (b) : (a)) 40 #define GETC(a, cntr) a[cntr++] 41 #define UNGETC(cntr) cntr-- 42 43 44 typedef struct usb_configrec { 45 char *selection; 46 int idVendor, idProduct, cfgndx; 47 char *serialno; 48 char *pathname; 49 char *driver; 50 } usb_configrec_t; 51 52 typedef enum { 53 USB_SELECTION, USB_VENDOR, USB_PRODUCT, USB_CFGNDX, USB_SRNO, 54 USB_PATH, USB_DRIVER, USB_NONE 55 } config_field_t; 56 57 typedef struct usbcfg_var { 58 const char *name; 59 config_field_t field; 60 } usbcfg_var_t; 61 62 static usbcfg_var_t usbcfg_varlist[] = { 63 { "selection", USB_SELECTION }, 64 { "idVendor", USB_VENDOR }, 65 { "idProduct", USB_PRODUCT }, 66 { "cfgndx", USB_CFGNDX }, 67 { "srno", USB_SRNO }, 68 { "pathname", USB_PATH }, 69 { "driver", USB_DRIVER }, 70 { NULL, USB_NONE } 71 }; 72 73 typedef enum { 74 EQUALS, 75 AMPERSAND, 76 BIT_OR, 77 STAR, 78 POUND, 79 COLON, 80 SEMICOLON, 81 COMMA, 82 SLASH, 83 WHITE_SPACE, 84 NEWLINE, 85 E_O_F, 86 STRING, 87 HEXVAL, 88 DECVAL, 89 NAME 90 } token_t; 91 92 93 static char usbconf_file[] = USBCONF_FILE; 94 static int linenum = 1; 95 static int cntr = 0; 96 static int frec = 0; 97 static int brec = 0; 98 static int btoken = 0; 99 mutex_t file_lock = DEFAULTMUTEX; 100 101 102 /* 103 * prototypes 104 */ 105 static int get_string(u_longlong_t *llptr, char *tchar); 106 static int getvalue(char *token, u_longlong_t *valuep); 107 108 109 /* 110 * The next item on the line is a string value. Allocate memory for 111 * it and copy the string. Return 1, and set arg ptr to newly allocated 112 * and initialized buffer, or NULL if an error occurs. 113 */ 114 static int 115 get_string(u_longlong_t *llptr, char *tchar) 116 { 117 register char *cp; 118 register char *start = NULL; 119 register int len = 0; 120 121 len = strlen(tchar); 122 start = tchar; 123 /* copy string */ 124 cp = calloc(len + 1, sizeof (char)); 125 if (cp == NULL) { 126 *llptr = 0; 127 128 return (0); 129 } 130 131 *llptr = (u_longlong_t)(uintptr_t)cp; 132 for (; len > 0; len--) { 133 /* convert some common escape sequences */ 134 if (*start == '\\') { 135 switch (*(start + 1)) { 136 case 't': 137 /* tab */ 138 *cp++ = '\t'; 139 len--; 140 start += 2; 141 break; 142 case 'n': 143 /* new line */ 144 *cp++ = '\n'; 145 len--; 146 start += 2; 147 break; 148 case 'b': 149 /* back space */ 150 *cp++ = '\b'; 151 len--; 152 start += 2; 153 break; 154 default: 155 /* simply copy it */ 156 *cp++ = *start++; 157 break; 158 } 159 } else { 160 *cp++ = *start++; 161 } 162 } 163 *cp = '\0'; 164 return (1); 165 } 166 167 168 /* 169 * get a decimal octal or hex number. Handle '~' for one's complement. 170 */ 171 static int 172 getvalue(char *token, u_longlong_t *valuep) 173 { 174 register int radix; 175 register u_longlong_t retval = 0; 176 register int onescompl = 0; 177 register int negate = 0; 178 register char c; 179 180 if (*token == '~') { 181 onescompl++; /* perform one's complement on result */ 182 token++; 183 } else if (*token == '-') { 184 negate++; 185 token++; 186 } 187 if (*token == '0') { 188 token++; 189 c = *token; 190 191 if (c == '\0') { 192 *valuep = 0; /* value is 0 */ 193 return (0); 194 } 195 196 if (c == 'x' || c == 'X') { 197 radix = 16; 198 token++; 199 } else { 200 radix = 8; 201 } 202 } else { 203 radix = 10; 204 } 205 206 while ((c = *token++)) { 207 switch (radix) { 208 case 8: 209 if (c >= '0' && c <= '7') { 210 c -= '0'; 211 } else { 212 return (-1); /* invalid number */ 213 } 214 retval = (retval << 3) + c; 215 break; 216 case 10: 217 if (c >= '0' && c <= '9') { 218 c -= '0'; 219 } else { 220 return (-1); /* invalid number */ 221 } 222 retval = (retval * 10) + c; 223 break; 224 case 16: 225 if (c >= 'a' && c <= 'f') { 226 c = c - 'a' + 10; 227 } else if (c >= 'A' && c <= 'F') { 228 c = c - 'A' + 10; 229 } else if (c >= '0' && c <= '9') { 230 c -= '0'; 231 } else { 232 return (-1); /* invalid number */ 233 } 234 retval = (retval << 4) + c; 235 break; 236 } 237 } 238 if (onescompl) 239 retval = ~retval; 240 if (negate) 241 retval = -retval; 242 *valuep = retval; 243 244 return (0); 245 } 246 247 /* 248 * returns the field from the token 249 */ 250 static config_field_t 251 usb_get_var_type(char *str) 252 { 253 usbcfg_var_t *cfgvar; 254 255 cfgvar = &usbcfg_varlist[0]; 256 while (cfgvar->field != USB_NONE) { 257 if (strcasecmp(cfgvar->name, str) == 0) { 258 break; 259 } else { 260 cfgvar++; 261 } 262 } 263 264 return (cfgvar->field); 265 } 266 267 268 /* ARGSUSED */ 269 static token_t 270 lex(char *buf, char *val, char **errmsg) 271 { 272 int ch, oval, badquote; 273 char *cp; 274 token_t token; 275 276 cp = val; 277 while ((ch = GETC(buf, cntr)) == ' ' || ch == '\t'); 278 279 /* 280 * Note the beginning of a token 281 */ 282 btoken = cntr - 1; 283 284 *cp++ = (char)ch; 285 switch (ch) { 286 case '=': 287 token = EQUALS; 288 break; 289 case '&': 290 token = AMPERSAND; 291 break; 292 case '|': 293 token = BIT_OR; 294 break; 295 case '*': 296 token = STAR; 297 break; 298 case '#': 299 token = POUND; 300 break; 301 case ':': 302 token = COLON; 303 break; 304 case ';': 305 token = SEMICOLON; 306 break; 307 case ',': 308 token = COMMA; 309 break; 310 case '/': 311 token = SLASH; 312 break; 313 case ' ': 314 case '\t': 315 case '\f': 316 while ((ch = GETC(buf, cntr)) == ' ' || 317 ch == '\t' || ch == '\f') 318 *cp++ = (char)ch; 319 (void) UNGETC(cntr); 320 token = WHITE_SPACE; 321 break; 322 case '\n': 323 case '\r': 324 token = NEWLINE; 325 break; 326 case '"': 327 cp--; 328 badquote = 0; 329 while (!badquote && (ch = GETC(buf, cntr)) != '"') { 330 switch (ch) { 331 case '\n': 332 case -1: 333 (void) snprintf(*errmsg, MAXPATHLEN, 334 "Missing \""); 335 cp = val; 336 *cp++ = '\n'; 337 badquote = 1; 338 /* since we consumed the newline/EOF */ 339 (void) UNGETC(cntr); 340 break; 341 342 case '\\': 343 ch = (char)GETC(buf, cntr); 344 if (!isdigit(ch)) { 345 /* escape the character */ 346 *cp++ = (char)ch; 347 break; 348 } 349 oval = 0; 350 while (ch >= '0' && ch <= '7') { 351 ch -= '0'; 352 oval = (oval << 3) + ch; 353 ch = (char)GETC(buf, cntr); 354 } 355 (void) UNGETC(cntr); 356 /* check for character overflow? */ 357 if (oval > 127) { 358 (void) snprintf(*errmsg, MAXPATHLEN, 359 "Character overflow detected.\n"); 360 } 361 *cp++ = (char)oval; 362 break; 363 default: 364 *cp++ = (char)ch; 365 break; 366 } 367 } 368 token = STRING; 369 break; 370 371 default: 372 if (ch == -1) { 373 token = EOF; 374 break; 375 } 376 /* 377 * detect a lone '-' (including at the end of a line), and 378 * identify it as a 'name' 379 */ 380 if (ch == '-') { 381 *cp++ = (char)(ch = GETC(buf, cntr)); 382 if (iswhite(ch) || (ch == '\n')) { 383 (void) UNGETC(cntr); 384 cp--; 385 token = NAME; 386 break; 387 } 388 } else if (isunary(ch)) { 389 *cp++ = (char)(ch = GETC(buf, cntr)); 390 } 391 392 if (isdigit(ch)) { 393 if (ch == '0') { 394 if ((ch = GETC(buf, cntr)) == 'x') { 395 *cp++ = (char)ch; 396 ch = GETC(buf, cntr); 397 while (isxdigit(ch)) { 398 *cp++ = (char)ch; 399 ch = GETC(buf, cntr); 400 } 401 (void) UNGETC(cntr); 402 token = HEXVAL; 403 } else { 404 goto digit; 405 } 406 } else { 407 ch = GETC(buf, cntr); 408 digit: 409 while (isdigit(ch)) { 410 *cp++ = (char)ch; 411 ch = GETC(buf, cntr); 412 } 413 (void) UNGETC(cntr); 414 token = DECVAL; 415 } 416 } else if (isalpha(ch) || ch == '\\') { 417 if (ch != '\\') { 418 ch = GETC(buf, cntr); 419 } else { 420 /* 421 * if the character was a backslash, 422 * back up so we can overwrite it with 423 * the next (i.e. escaped) character. 424 */ 425 cp--; 426 } 427 428 while (isnamechar(ch) || ch == '\\') { 429 if (ch == '\\') 430 ch = GETC(buf, cntr); 431 *cp++ = (char)ch; 432 ch = GETC(buf, cntr); 433 } 434 (void) UNGETC(cntr); 435 token = NAME; 436 } else { 437 438 return (-1); 439 } 440 break; 441 } 442 *cp = '\0'; 443 444 return (token); 445 } 446 447 448 /* 449 * Leave NEWLINE as the next character. 450 */ 451 static void 452 find_eol(char *buf) 453 { 454 register int ch; 455 456 while ((ch = GETC(buf, cntr)) != -1) { 457 if (isnewline(ch)) { 458 (void) UNGETC(cntr); 459 break; 460 } 461 } 462 } 463 464 465 /* 466 * Fetch one record from the USBCONF_FILE 467 */ 468 static token_t 469 usb_get_conf_rec(char *buf, usb_configrec_t **rec, char **errmsg) 470 { 471 token_t token; 472 char tokval[MAXLINESIZE]; 473 usb_configrec_t *user_rec; 474 config_field_t cfgvar; 475 u_longlong_t llptr; 476 u_longlong_t value; 477 boolean_t sor = B_TRUE; 478 479 enum { 480 USB_NEWVAR, USB_CONFIG_VAR, USB_VAR_EQUAL, USB_VAR_VALUE, 481 USB_ERROR 482 } parse_state = USB_NEWVAR; 483 484 DPRINTF("usb_get_conf_rec:\n"); 485 486 user_rec = (usb_configrec_t *)calloc(1, sizeof (usb_configrec_t)); 487 if (user_rec == (usb_configrec_t *)NULL) { 488 return (0); 489 } 490 491 user_rec->idVendor = user_rec->idProduct = user_rec->cfgndx = -1; 492 493 token = lex(buf, tokval, errmsg); 494 while ((token != EOF) && (token != SEMICOLON)) { 495 switch (token) { 496 case STAR: 497 case POUND: 498 /* skip comments */ 499 find_eol(buf); 500 break; 501 case NEWLINE: 502 linenum++; 503 break; 504 case NAME: 505 case STRING: 506 switch (parse_state) { 507 case USB_NEWVAR: 508 cfgvar = usb_get_var_type(tokval); 509 if (cfgvar == USB_NONE) { 510 parse_state = USB_ERROR; 511 (void) snprintf(*errmsg, MAXPATHLEN, 512 "Syntax Error: Invalid field %s", 513 tokval); 514 } else { 515 /* 516 * Note the beginning of a record 517 */ 518 if (sor) { 519 brec = btoken; 520 if (frec == 0) frec = brec; 521 sor = B_FALSE; 522 } 523 parse_state = USB_CONFIG_VAR; 524 } 525 break; 526 case USB_VAR_VALUE: 527 if ((cfgvar == USB_VENDOR) || 528 (cfgvar == USB_PRODUCT) || 529 (cfgvar == USB_CFGNDX)) { 530 parse_state = USB_ERROR; 531 (void) snprintf(*errmsg, MAXPATHLEN, 532 "Syntax Error: Invalid value %s " 533 "for field: %s\n", tokval, 534 usbcfg_varlist[cfgvar].name); 535 } else if (get_string(&llptr, tokval)) { 536 switch (cfgvar) { 537 case USB_SELECTION: 538 user_rec->selection = 539 (char *)(uintptr_t)llptr; 540 parse_state = USB_NEWVAR; 541 break; 542 case USB_SRNO: 543 user_rec->serialno = 544 (char *)(uintptr_t)llptr; 545 parse_state = USB_NEWVAR; 546 break; 547 case USB_PATH: 548 user_rec->pathname = 549 (char *)(uintptr_t)llptr; 550 parse_state = USB_NEWVAR; 551 break; 552 case USB_DRIVER: 553 user_rec->driver = 554 (char *)(uintptr_t)llptr; 555 parse_state = USB_NEWVAR; 556 break; 557 default: 558 parse_state = USB_ERROR; 559 free((void *)(uintptr_t)llptr); 560 } 561 } else { 562 parse_state = USB_ERROR; 563 (void) snprintf(*errmsg, MAXPATHLEN, 564 "Syntax Error: Invalid value %s " 565 "for field: %s\n", tokval, 566 usbcfg_varlist[cfgvar].name); 567 } 568 break; 569 case USB_ERROR: 570 /* just skip */ 571 break; 572 default: 573 parse_state = USB_ERROR; 574 (void) snprintf(*errmsg, MAXPATHLEN, 575 "Syntax Error: at %s", tokval); 576 break; 577 } 578 break; 579 case EQUALS: 580 if (parse_state == USB_CONFIG_VAR) { 581 if (cfgvar == USB_NONE) { 582 parse_state = USB_ERROR; 583 (void) snprintf(*errmsg, MAXPATHLEN, 584 "Syntax Error: unexpected '='"); 585 } else { 586 parse_state = USB_VAR_VALUE; 587 } 588 } else if (parse_state != USB_ERROR) { 589 (void) snprintf(*errmsg, MAXPATHLEN, 590 "Syntax Error: unexpected '='"); 591 parse_state = USB_ERROR; 592 } 593 break; 594 case HEXVAL: 595 case DECVAL: 596 if ((parse_state == USB_VAR_VALUE) && (cfgvar != 597 USB_NONE)) { 598 (void) getvalue(tokval, &value); 599 switch (cfgvar) { 600 case USB_VENDOR: 601 user_rec->idVendor = (int)value; 602 parse_state = USB_NEWVAR; 603 break; 604 case USB_PRODUCT: 605 user_rec->idProduct = (int)value; 606 parse_state = USB_NEWVAR; 607 break; 608 case USB_CFGNDX: 609 user_rec->cfgndx = (int)value; 610 parse_state = USB_NEWVAR; 611 break; 612 default: 613 (void) snprintf(*errmsg, MAXPATHLEN, 614 "Syntax Error: Invalid value for " 615 "%s", usbcfg_varlist[cfgvar].name); 616 } 617 } else if (parse_state != USB_ERROR) { 618 parse_state = USB_ERROR; 619 (void) snprintf(*errmsg, MAXPATHLEN, 620 "Syntax Error: unexpected hex/decimal: %s", 621 tokval); 622 } 623 break; 624 default: 625 (void) snprintf(*errmsg, MAXPATHLEN, 626 "Syntax Error: at: %s", tokval); 627 parse_state = USB_ERROR; 628 break; 629 } 630 token = lex(buf, tokval, errmsg); 631 } 632 *rec = user_rec; 633 634 return (token); 635 } 636 637 638 /* 639 * Here we compare the two records and determine if they are the same 640 */ 641 static boolean_t 642 usb_cmp_rec(usb_configrec_t *cfg_rec, usb_configrec_t *user_rec) 643 { 644 char *ustr, *cstr; 645 boolean_t srno = B_FALSE, path = B_FALSE; 646 647 DPRINTF("usb_cmp_rec:\n"); 648 649 if ((cfg_rec->idVendor == user_rec->idVendor) && 650 (cfg_rec->idProduct == user_rec->idProduct)) { 651 if (user_rec->serialno) { 652 if (cfg_rec->serialno) { 653 srno = (strcmp(cfg_rec->serialno, 654 user_rec->serialno) == 0); 655 } else { 656 657 return (B_FALSE); 658 } 659 660 } else if (user_rec->pathname) { 661 if (cfg_rec->pathname) { 662 /* 663 * Comparing on this is tricky. At this point 664 * hubd knows: ../hubd@P/device@P while user 665 * will specify ..../hubd@P/keyboard@P 666 * First compare till .../hubd@P 667 * Second compare is just P in "device@P" 668 * 669 * XXX: note that we assume P as one character 670 * as there are no 2 digit hubs in the market. 671 */ 672 ustr = strrchr(user_rec->pathname, '/'); 673 cstr = strrchr(cfg_rec->pathname, '/'); 674 path = (strncmp(cfg_rec->pathname, 675 user_rec->pathname, 676 MAX(ustr - user_rec->pathname, 677 cstr - cfg_rec->pathname)) == 0); 678 path = path && (*(user_rec->pathname + 679 strlen(user_rec->pathname) -1) == 680 *(cfg_rec->pathname + 681 strlen(cfg_rec->pathname) - 1)); 682 } else { 683 684 return (B_FALSE); 685 } 686 687 } else if (cfg_rec->serialno || cfg_rec->pathname) { 688 689 return (B_FALSE); 690 } else { 691 692 return (B_TRUE); 693 } 694 695 return (srno || path); 696 } else { 697 698 return (B_FALSE); 699 } 700 } 701 702 703 /* 704 * free the record allocated in usb_get_conf_rec 705 */ 706 static void 707 usb_free_rec(usb_configrec_t *rec) 708 { 709 if (rec == (usb_configrec_t *)NULL) { 710 711 return; 712 } 713 714 free(rec->selection); 715 free(rec->serialno); 716 free(rec->pathname); 717 free(rec->driver); 718 free(rec); 719 } 720 721 722 int 723 add_entry(char *selection, int vid, int pid, int cfgndx, char *srno, 724 char *path, char *driver, char **errmsg) 725 { 726 int file; 727 int rval = CFGA_USB_OK; 728 char *buf = (char *)NULL; 729 char str[MAXLINESIZE]; 730 token_t token = NEWLINE; 731 boolean_t found = B_FALSE; 732 struct stat st; 733 usb_configrec_t cfgrec, *user_rec = NULL; 734 735 DPRINTF("add_entry: driver=%s, path=%s\n", 736 driver ? driver : "", path ? path : ""); 737 738 if (*errmsg == (char *)NULL) { 739 if ((*errmsg = calloc(MAXPATHLEN, 1)) == (char *)NULL) { 740 741 return (CFGA_USB_CONFIG_FILE); 742 } 743 } 744 745 (void) mutex_lock(&file_lock); 746 747 /* Initialize the cfgrec */ 748 cfgrec.selection = selection; 749 cfgrec.idVendor = vid; 750 cfgrec.idProduct = pid; 751 cfgrec.cfgndx = cfgndx; 752 cfgrec.serialno = srno; 753 cfgrec.pathname = path; 754 cfgrec.driver = driver; 755 756 /* open config_map.conf file */ 757 file = open(usbconf_file, O_RDWR, 0666); 758 if (file == -1) { 759 (void) snprintf(*errmsg, MAXPATHLEN, 760 "failed to open config file\n"); 761 (void) mutex_unlock(&file_lock); 762 763 return (CFGA_USB_CONFIG_FILE); 764 } 765 766 if (lockf(file, F_TLOCK, 0) == -1) { 767 (void) snprintf(*errmsg, MAXPATHLEN, 768 "failed to lock config file\n"); 769 close(file); 770 (void) mutex_unlock(&file_lock); 771 772 return (CFGA_USB_LOCK_FILE); 773 } 774 775 /* 776 * These variables need to be reinitialized here as they may 777 * have been modified by a previous thread that called this 778 * function 779 */ 780 linenum = 1; 781 cntr = 0; 782 frec = 0; 783 brec = 0; 784 btoken = 0; 785 786 if (fstat(file, &st) != 0) { 787 DPRINTF("add_entry: failed to fstat config file\n"); 788 rval = CFGA_USB_CONFIG_FILE; 789 goto exit; 790 } 791 792 if ((buf = (char *)malloc(st.st_size)) == NULL) { 793 DPRINTF("add_entry: failed to fstat config file\n"); 794 rval = CFGA_USB_ALLOC_FAIL; 795 goto exit; 796 } 797 798 if (st.st_size != read(file, buf, st.st_size)) { 799 DPRINTF("add_entry: failed to read config file\n"); 800 rval = CFGA_USB_CONFIG_FILE; 801 goto exit; 802 } 803 804 /* set up for reading the file */ 805 806 while ((token != EOF) && !found) { 807 if (user_rec) { 808 usb_free_rec(user_rec); 809 user_rec = NULL; 810 } 811 token = usb_get_conf_rec(buf, &user_rec, errmsg); 812 found = usb_cmp_rec(&cfgrec, user_rec); 813 DPRINTF("add_entry: token=%x, found=%x\n", token, found); 814 } 815 816 bzero(str, MAXLINESIZE); 817 818 if (found) { 819 DPRINTF("FOUND\n"); 820 (void) snprintf(str, MAXLINESIZE, "selection=%s idVendor=0x%x " 821 "idProduct=0x%x ", 822 (cfgrec.selection) ? cfgrec.selection : user_rec->selection, 823 user_rec->idVendor, user_rec->idProduct); 824 825 if ((user_rec->cfgndx != -1) || (cfgrec.cfgndx != -1)) { 826 (void) snprintf(&str[strlen(str)], MAXLINESIZE, 827 "cfgndx=0x%x ", (cfgrec.cfgndx != -1) ? 828 cfgrec.cfgndx : user_rec->cfgndx); 829 } 830 831 if (user_rec->serialno) { 832 (void) snprintf(&str[strlen(str)], MAXLINESIZE, 833 "srno=\"%s\" ", user_rec->serialno); 834 } 835 836 if (user_rec->pathname) { 837 (void) snprintf(&str[strlen(str)], MAXLINESIZE, 838 "pathname=\"%s\" ", user_rec->pathname); 839 } 840 841 if (user_rec->driver) { 842 (void) snprintf(&str[strlen(str)], MAXLINESIZE, 843 "driver=\"%s\" ", user_rec->driver); 844 } else if (cfgrec.driver != NULL) { 845 if (strlen(cfgrec.driver)) { 846 (void) snprintf(&str[strlen(str)], MAXLINESIZE, 847 "driver=\"%s\" ", cfgrec.driver); 848 } 849 } 850 851 (void) strlcat(str, ";", sizeof (str)); 852 853 /* 854 * Seek to the beginning of the record 855 */ 856 if (lseek(file, brec, SEEK_SET) == -1) { 857 DPRINTF("add_entry: failed to lseek config file\n"); 858 rval = CFGA_USB_CONFIG_FILE; 859 goto exit; 860 } 861 862 /* 863 * Write the modified record 864 */ 865 if (write(file, str, strlen(str)) == -1) { 866 DPRINTF("add_entry: failed to write config file\n"); 867 rval = CFGA_USB_CONFIG_FILE; 868 goto exit; 869 } 870 871 /* 872 * Write the rest of the file as it was 873 */ 874 if (write(file, buf+cntr, st.st_size - cntr) == -1) { 875 DPRINTF("add_entry: failed to write config file\n"); 876 rval = CFGA_USB_CONFIG_FILE; 877 goto exit; 878 } 879 880 } else { 881 DPRINTF("!FOUND\n"); 882 (void) snprintf(str, MAXLINESIZE, 883 "selection=%s idVendor=0x%x idProduct=0x%x ", 884 (cfgrec.selection) ? cfgrec.selection : "enable", 885 cfgrec.idVendor, cfgrec.idProduct); 886 887 if (cfgrec.cfgndx != -1) { 888 (void) snprintf(&str[strlen(str)], MAXLINESIZE, 889 "cfgndx=0x%x ", cfgrec.cfgndx); 890 } 891 892 if (cfgrec.serialno) { 893 (void) snprintf(&str[strlen(str)], MAXLINESIZE, 894 "srno=\"%s\" ", cfgrec.serialno); 895 } 896 897 if (cfgrec.pathname != NULL) { 898 (void) snprintf(&str[strlen(str)], MAXLINESIZE, 899 "pathname=\"%s\" ", cfgrec.pathname); 900 } 901 902 if (cfgrec.driver != NULL) { 903 if (strlen(cfgrec.driver)) { 904 (void) snprintf(&str[strlen(str)], MAXLINESIZE, 905 "driver=\"%s\" ", cfgrec.driver); 906 } 907 } 908 909 (void) strlcat(str, ";\n", sizeof (str)); 910 911 /* 912 * Incase this is the first entry, add it after the comments 913 */ 914 if (frec == 0) { 915 frec = st.st_size; 916 } 917 918 /* 919 * Go to the beginning of the records 920 */ 921 if (lseek(file, frec, SEEK_SET) == -1) { 922 DPRINTF("add_entry: failed to lseek config file\n"); 923 rval = CFGA_USB_CONFIG_FILE; 924 goto exit; 925 } 926 927 /* 928 * Add the entry 929 */ 930 if (write(file, str, strlen(str)) == -1) { 931 DPRINTF("add_entry: failed to write config file\n"); 932 rval = CFGA_USB_CONFIG_FILE; 933 goto exit; 934 } 935 936 /* 937 * write the remaining file as it was 938 */ 939 if (write(file, buf+frec, st.st_size - frec) == -1) { 940 DPRINTF("add_entry: failed to write config file\n"); 941 rval = CFGA_USB_CONFIG_FILE; 942 goto exit; 943 } 944 } 945 946 /* no error encountered */ 947 if (rval == CFGA_USB_OK) { 948 free(errmsg); 949 } 950 951 exit: 952 if (buf != NULL) { 953 free(buf); 954 } 955 956 if (lockf(file, F_ULOCK, 0) == -1) { 957 DPRINTF("add_entry: failed to unlock config file\n"); 958 959 rval = CFGA_USB_LOCK_FILE; 960 } 961 962 close(file); 963 964 (void) mutex_unlock(&file_lock); 965 966 return (rval); 967 } 968