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