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