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
get_string(u_longlong_t * llptr,char * tchar)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
getvalue(char * token,u_longlong_t * valuep)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
usb_get_var_type(char * str)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
lex(char * buf,char * val,char ** errmsg)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
find_eol(char * buf)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
usb_get_conf_rec(char * buf,usb_configrec_t ** rec,char ** errmsg)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
usb_cmp_rec(usb_configrec_t * cfg_rec,usb_configrec_t * user_rec)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
usb_free_rec(usb_configrec_t * rec)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
add_entry(char * selection,int vid,int pid,int cfgndx,char * srno,char * path,char * driver,char ** errmsg)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