subr_userconf.c revision 1.16 1 /* $NetBSD: subr_userconf.c,v 1.16 2005/08/25 15:06:28 drochner Exp $ */
2
3 /*
4 * Copyright (c) 1996 Mats O Jansson <moj (at) stacken.kth.se>
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. All advertising materials mentioning features or use of this software
16 * must display the following acknowledgement:
17 * This product includes software developed by Mats O Jansson.
18 * 4. The name of the author may not be used to endorse or promote
19 * products derived from this software without specific prior written
20 * permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
23 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
24 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
26 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE.
33 *
34 * OpenBSD: subr_userconf.c,v 1.19 2000/01/08 23:23:37 d Exp
35 */
36
37 #include <sys/cdefs.h>
38 __KERNEL_RCSID(0, "$NetBSD: subr_userconf.c,v 1.16 2005/08/25 15:06:28 drochner Exp $");
39
40 #include "opt_userconf.h"
41
42 #include <sys/param.h>
43 #include <sys/systm.h>
44 #include <sys/device.h>
45 #include <sys/malloc.h>
46 #include <sys/time.h>
47
48 #include <dev/cons.h>
49
50 extern struct cfdata cfdata[];
51
52 static int userconf_base = 16; /* Base for "large" numbers */
53 static int userconf_maxdev = -1; /* # of used device slots */
54 static int userconf_totdev = -1; /* # of device slots */
55 #if 0
56 static int userconf_maxlocnames = -1; /* # of locnames */
57 #endif
58 static int userconf_cnt = -1; /* Line counter for ... */
59 static int userconf_lines = 12; /* ... # of lines per page */
60 static int userconf_histlen = 0;
61 static int userconf_histcur = 0;
62 static char userconf_history[1024];
63 static int userconf_histsz = sizeof(userconf_history);
64 static char userconf_argbuf[40]; /* Additional input */
65 static char userconf_cmdbuf[40]; /* Command line */
66 static char userconf_histbuf[40];
67
68 static int getsn(char *, int);
69
70 #define UC_CHANGE 'c'
71 #define UC_DISABLE 'd'
72 #define UC_ENABLE 'e'
73 #define UC_FIND 'f'
74 #define UC_SHOW 's'
75
76 static const char *userconf_cmds[] = {
77 "base", "b",
78 "change", "c",
79 "disable", "d",
80 "enable", "e",
81 "exit", "q",
82 "find", "f",
83 "help", "h",
84 "list", "l",
85 "lines", "L",
86 "quit", "q",
87 "?", "h",
88 "", "",
89 };
90
91 static void
92 userconf_init(void)
93 {
94 int i;
95 struct cfdata *cf;
96
97 i = 0;
98 for (cf = cfdata; cf->cf_name; cf++)
99 i++;
100
101 userconf_maxdev = i - 1;
102 userconf_totdev = i - 1;
103 }
104
105 static int
106 userconf_more(void)
107 {
108 int quit = 0;
109 char c = '\0';
110
111 if (userconf_cnt != -1) {
112 if (userconf_cnt == userconf_lines) {
113 printf("-- more --");
114 c = cngetc();
115 userconf_cnt = 0;
116 printf("\r \r");
117 }
118 userconf_cnt++;
119 if (c == 'q' || c == 'Q')
120 quit = 1;
121 }
122 return (quit);
123 }
124
125 static void
126 userconf_hist_cmd(char cmd)
127 {
128 userconf_histcur = userconf_histlen;
129 if (userconf_histcur < userconf_histsz) {
130 userconf_history[userconf_histcur] = cmd;
131 userconf_histcur++;
132 }
133 }
134
135 static void
136 userconf_hist_int(int val)
137 {
138 snprintf(userconf_histbuf, sizeof(userconf_histbuf), " %d", val);
139 if ((userconf_histcur + strlen(userconf_histbuf)) < userconf_histsz) {
140 memcpy(&userconf_history[userconf_histcur],
141 userconf_histbuf,
142 strlen(userconf_histbuf));
143 userconf_histcur = userconf_histcur + strlen(userconf_histbuf);
144 }
145 }
146
147 static void
148 userconf_hist_eoc(void)
149 {
150 if (userconf_histcur < userconf_histsz) {
151 userconf_history[userconf_histcur] = '\n';
152 userconf_histcur++;
153 userconf_histlen = userconf_histcur;
154 }
155 }
156
157 static void
158 userconf_pnum(int val)
159 {
160 if (val > -2 && val < 16) {
161 printf("%d",val);
162 } else {
163 switch (userconf_base) {
164 case 8:
165 printf("0%o",val);
166 break;
167 case 10:
168 printf("%d",val);
169 break;
170 case 16:
171 default:
172 printf("0x%x",val);
173 break;
174 }
175 }
176 }
177
178 static void
179 userconf_pdevnam(short dev)
180 {
181 struct cfdata *cd;
182
183 cd = &cfdata[dev];
184 printf("%s", cd->cf_name);
185 switch (cd->cf_fstate) {
186 case FSTATE_NOTFOUND:
187 case FSTATE_DNOTFOUND:
188 printf("%d", cd->cf_unit);
189 break;
190 case FSTATE_FOUND:
191 printf("*FOUND*");
192 break;
193 case FSTATE_STAR:
194 case FSTATE_DSTAR:
195 printf("*");
196 break;
197 default:
198 printf("*UNKNOWN*");
199 break;
200 }
201 }
202
203 static void
204 userconf_pdev(short devno)
205 {
206 struct cfdata *cd;
207 const struct cfparent *cfp;
208 int *l;
209 const struct cfiattrdata *ia;
210 const struct cflocdesc *ld;
211 int nld, i;
212
213 if (devno > userconf_maxdev) {
214 printf("Unknown devno (max is %d)\n", userconf_maxdev);
215 return;
216 }
217
218 cd = &cfdata[devno];
219
220 printf("[%3d] ", devno);
221 userconf_pdevnam(devno);
222 printf(" at");
223 cfp = cd->cf_pspec;
224 if (cfp == NULL)
225 printf(" root");
226 else if (cfp->cfp_parent != NULL && cfp->cfp_unit != -1)
227 printf(" %s%d", cfp->cfp_parent, cfp->cfp_unit);
228 else
229 printf(" %s?", cfp->cfp_parent != NULL ? cfp->cfp_parent
230 : cfp->cfp_iattr);
231 switch (cd->cf_fstate) {
232 case FSTATE_NOTFOUND:
233 case FSTATE_FOUND:
234 case FSTATE_STAR:
235 break;
236 case FSTATE_DNOTFOUND:
237 case FSTATE_DSTAR:
238 printf(" disable");
239 break;
240 default:
241 printf(" ???");
242 break;
243 }
244 if (cfp) {
245 l = cd->cf_loc;
246 ia = cfiattr_lookup(cfp->cfp_iattr, 0);
247 KASSERT(ia);
248 ld = ia->ci_locdesc;
249 nld = ia->ci_loclen;
250 for (i = 0; i < nld; i++) {
251 printf(" %s ", ld[i].cld_name);
252 userconf_pnum(*l++);
253 }
254 }
255 printf("\n");
256 }
257
258 static int
259 userconf_number(char *c, int *val)
260 {
261 u_int num = 0;
262 int neg = 0;
263 int base = 10;
264
265 if (*c == '-') {
266 neg = 1;
267 c++;
268 }
269 if (*c == '0') {
270 base = 8;
271 c++;
272 if (*c == 'x' || *c == 'X') {
273 base = 16;
274 c++;
275 }
276 }
277 while (*c != '\n' && *c != '\t' && *c != ' ' && *c != '\0') {
278 u_char cc = *c;
279
280 if (cc >= '0' && cc <= '9')
281 cc = cc - '0';
282 else if (cc >= 'a' && cc <= 'f')
283 cc = cc - 'a' + 10;
284 else if (cc >= 'A' && cc <= 'F')
285 cc = cc - 'A' + 10;
286 else
287 return (-1);
288
289 if (cc > base)
290 return (-1);
291 num = num * base + cc;
292 c++;
293 }
294
295 if (neg && num > INT_MAX) /* overflow */
296 return (1);
297 *val = neg ? - num : num;
298 return (0);
299 }
300
301 static int
302 userconf_device(char *cmd, int *len, short *unit, short *state)
303 {
304 short u = 0, s = FSTATE_FOUND;
305 int l = 0;
306 char *c;
307
308 c = cmd;
309 while (*c >= 'a' && *c <= 'z') {
310 l++;
311 c++;
312 }
313 if (*c == '*') {
314 s = FSTATE_STAR;
315 c++;
316 } else {
317 while (*c >= '0' && *c <= '9') {
318 s = FSTATE_NOTFOUND;
319 u = u*10 + *c - '0';
320 c++;
321 }
322 }
323 while (*c == ' ' || *c == '\t' || *c == '\n')
324 c++;
325
326 if (*c == '\0') {
327 *len = l;
328 *unit = u;
329 *state = s;
330 return(0);
331 }
332
333 return(-1);
334 }
335
336 static void
337 userconf_modify(const char *item, int *val)
338 {
339 int ok = 0;
340 int a;
341 char *c;
342
343 while (!ok) {
344 printf("%s [", item);
345 userconf_pnum(*val);
346 printf("] ? ");
347
348 getsn(userconf_argbuf, sizeof(userconf_argbuf));
349
350 c = userconf_argbuf;
351 while (*c == ' ' || *c == '\t' || *c == '\n') c++;
352
353 if (*c != '\0') {
354 if (userconf_number(c, &a) == 0) {
355 *val = a;
356 ok = 1;
357 } else {
358 printf("Unknown argument\n");
359 }
360 } else {
361 ok = 1;
362 }
363 }
364 }
365
366 static void
367 userconf_change(int devno)
368 {
369 struct cfdata *cd;
370 char c = '\0';
371 int *l;
372 int ln;
373 const struct cfiattrdata *ia;
374 const struct cflocdesc *ld;
375 int nld;
376
377 if (devno <= userconf_maxdev) {
378
379 userconf_pdev(devno);
380
381 while (c != 'y' && c != 'Y' && c != 'n' && c != 'N') {
382 printf("change (y/n) ?");
383 c = cngetc();
384 printf("\n");
385 }
386
387 if (c == 'y' || c == 'Y') {
388
389 /* XXX add cmd 'c' <devno> */
390 userconf_hist_cmd('c');
391 userconf_hist_int(devno);
392
393 cd = &cfdata[devno];
394 l = cd->cf_loc;
395 ia = cfiattr_lookup(cd->cf_pspec->cfp_iattr, 0);
396 KASSERT(ia);
397 ld = ia->ci_locdesc;
398 nld = ia->ci_loclen;
399
400 for (ln = 0; ln < nld; ln++)
401 {
402 userconf_modify(ld[ln].cld_name, l);
403
404 /* XXX add *l */
405 userconf_hist_int(*l);
406
407 l++;
408 }
409
410 printf("[%3d] ", devno);
411 userconf_pdevnam(devno);
412 printf(" changed\n");
413 userconf_pdev(devno);
414
415 /* XXX add eoc */
416 userconf_hist_eoc();
417
418 }
419 } else {
420 printf("Unknown devno (max is %d)\n", userconf_maxdev);
421 }
422 }
423
424 static void
425 userconf_disable(int devno)
426 {
427 int done = 0;
428
429 if (devno <= userconf_maxdev) {
430 switch (cfdata[devno].cf_fstate) {
431 case FSTATE_NOTFOUND:
432 cfdata[devno].cf_fstate = FSTATE_DNOTFOUND;
433 break;
434 case FSTATE_STAR:
435 cfdata[devno].cf_fstate = FSTATE_DSTAR;
436 break;
437 case FSTATE_DNOTFOUND:
438 case FSTATE_DSTAR:
439 done = 1;
440 break;
441 default:
442 printf("Error unknown state\n");
443 break;
444 }
445
446 printf("[%3d] ", devno);
447 userconf_pdevnam(devno);
448 if (done) {
449 printf(" already");
450 } else {
451 /* XXX add cmd 'd' <devno> eoc */
452 userconf_hist_cmd('d');
453 userconf_hist_int(devno);
454 userconf_hist_eoc();
455 }
456 printf(" disabled\n");
457 } else {
458 printf("Unknown devno (max is %d)\n", userconf_maxdev);
459 }
460 }
461
462 static void
463 userconf_enable(int devno)
464 {
465 int done = 0;
466
467 if (devno <= userconf_maxdev) {
468 switch (cfdata[devno].cf_fstate) {
469 case FSTATE_DNOTFOUND:
470 cfdata[devno].cf_fstate = FSTATE_NOTFOUND;
471 break;
472 case FSTATE_DSTAR:
473 cfdata[devno].cf_fstate = FSTATE_STAR;
474 break;
475 case FSTATE_NOTFOUND:
476 case FSTATE_STAR:
477 done = 1;
478 break;
479 default:
480 printf("Error unknown state\n");
481 break;
482 }
483
484 printf("[%3d] ", devno);
485 userconf_pdevnam(devno);
486 if (done) {
487 printf(" already");
488 } else {
489 /* XXX add cmd 'e' <devno> eoc */
490 userconf_hist_cmd('d');
491 userconf_hist_int(devno);
492 userconf_hist_eoc();
493 }
494 printf(" enabled\n");
495 } else {
496 printf("Unknown devno (max is %d)\n", userconf_maxdev);
497 }
498 }
499
500 static void
501 userconf_help(void)
502 {
503 int j = 0, k;
504
505 printf("command args description\n");
506 while (*userconf_cmds[j] != '\0') {
507 printf(userconf_cmds[j]);
508 k = strlen(userconf_cmds[j]);
509 while (k < 10) {
510 printf(" ");
511 k++;
512 }
513 switch (*userconf_cmds[j+1]) {
514 case 'L':
515 printf("[count] number of lines before more");
516 break;
517 case 'b':
518 printf("8|10|16 base on large numbers");
519 break;
520 case 'c':
521 printf("devno|dev change devices");
522 break;
523 case 'd':
524 printf("devno|dev disable devices");
525 break;
526 case 'e':
527 printf("devno|dev enable devices");
528 break;
529 case 'f':
530 printf("devno|dev find devices");
531 break;
532 case 'h':
533 printf(" this message");
534 break;
535 case 'l':
536 printf(" list configuration");
537 break;
538 case 'q':
539 printf(" leave userconf");
540 break;
541 default:
542 printf(" don't know");
543 break;
544 }
545 printf("\n");
546 j += 2;
547 }
548 }
549
550 static void
551 userconf_list(void)
552 {
553 int i = 0;
554
555 userconf_cnt = 0;
556
557 while (cfdata[i].cf_name != NULL) {
558 if (userconf_more())
559 break;
560 userconf_pdev(i++);
561 }
562
563 userconf_cnt = -1;
564 }
565
566 static void
567 userconf_common_dev(char *dev, int len, short unit, short state, char routine)
568 {
569 int i = 0;
570
571 switch (routine) {
572 case UC_CHANGE:
573 break;
574 default:
575 userconf_cnt = 0;
576 break;
577 }
578
579 while (cfdata[i].cf_name != NULL) {
580 if (strlen(cfdata[i].cf_name) == len) {
581
582 /*
583 * Ok, if device name is correct
584 * If state == FSTATE_FOUND, look for "dev"
585 * If state == FSTATE_STAR, look for "dev*"
586 * If state == FSTATE_NOTFOUND, look for "dev0"
587 */
588 if (strncasecmp(dev, cfdata[i].cf_name,
589 len) == 0 &&
590 (state == FSTATE_FOUND ||
591 (state == FSTATE_STAR &&
592 (cfdata[i].cf_fstate == FSTATE_STAR ||
593 cfdata[i].cf_fstate == FSTATE_DSTAR)) ||
594 (state == FSTATE_NOTFOUND &&
595 cfdata[i].cf_unit == unit &&
596 (cfdata[i].cf_fstate == FSTATE_NOTFOUND ||
597 cfdata[i].cf_fstate == FSTATE_DNOTFOUND)))) {
598 if (userconf_more())
599 break;
600 switch (routine) {
601 case UC_CHANGE:
602 userconf_change(i);
603 break;
604 case UC_ENABLE:
605 userconf_enable(i);
606 break;
607 case UC_DISABLE:
608 userconf_disable(i);
609 break;
610 case UC_FIND:
611 userconf_pdev(i);
612 break;
613 default:
614 printf("Unknown routine /%c/\n",
615 routine);
616 break;
617 }
618 }
619 }
620 i++;
621 }
622
623 switch (routine) {
624 case UC_CHANGE:
625 break;
626 default:
627 userconf_cnt = -1;
628 break;
629 }
630 }
631
632 #if 0
633 static void
634 userconf_add_read(char *prompt, char field, char *dev, int len, int *val)
635 {
636 int ok = 0;
637 int a;
638 char *c;
639
640 *val = -1;
641
642 while (!ok) {
643 printf("%s ? ", prompt);
644
645 getsn(userconf_argbuf, sizeof(userconf_argbuf));
646
647 c = userconf_argbuf;
648 while (*c == ' ' || *c == '\t' || *c == '\n') c++;
649
650 if (*c != '\0') {
651 if (userconf_number(c, &a) == 0) {
652 if (a > userconf_maxdev) {
653 printf("Unknown devno (max is %d)\n",
654 userconf_maxdev);
655 } else if (strncasecmp(dev,
656 cfdata[a].cf_name, len) != 0 &&
657 field == 'a') {
658 printf("Not same device type\n");
659 } else {
660 *val = a;
661 ok = 1;
662 }
663 } else if (*c == '?') {
664 userconf_common_dev(dev, len, 0,
665 FSTATE_FOUND, UC_FIND);
666 } else if (*c == 'q' || *c == 'Q') {
667 ok = 1;
668 } else {
669 printf("Unknown argument\n");
670 }
671 } else {
672 ok = 1;
673 }
674 }
675 }
676 #endif /* 0 */
677
678 static int
679 userconf_parse(char *cmd)
680 {
681 char *c, *v;
682 int i = 0, j = 0, k, a;
683 short unit, state;
684
685 c = cmd;
686 while (*c == ' ' || *c == '\t')
687 c++;
688 v = c;
689 while (*c != ' ' && *c != '\t' && *c != '\n' && *c != '\0') {
690 c++;
691 i++;
692 }
693
694 k = -1;
695 while (*userconf_cmds[j] != '\0') {
696 if (strlen(userconf_cmds[j]) == i) {
697 if (strncasecmp(v, userconf_cmds[j], i) == 0)
698 k = j;
699 }
700 j += 2;
701 }
702
703 while (*c == ' ' || *c == '\t' || *c == '\n')
704 c++;
705
706 if (k == -1) {
707 if (*v != '\n')
708 printf("Unknown command, try help\n");
709 } else {
710 switch (*userconf_cmds[k+1]) {
711 case 'L':
712 if (*c == '\0')
713 printf("Argument expected\n");
714 else if (userconf_number(c, &a) == 0)
715 userconf_lines = a;
716 else
717 printf("Unknown argument\n");
718 break;
719 case 'b':
720 if (*c == '\0')
721 printf("8|10|16 expected\n");
722 else if (userconf_number(c, &a) == 0) {
723 if (a == 8 || a == 10 || a == 16) {
724 userconf_base = a;
725 } else {
726 printf("8|10|16 expected\n");
727 }
728 } else
729 printf("Unknown argument\n");
730 break;
731 case 'c':
732 if (*c == '\0')
733 printf("DevNo or Dev expected\n");
734 else if (userconf_number(c, &a) == 0)
735 userconf_change(a);
736 else if (userconf_device(c, &a, &unit, &state) == 0)
737 userconf_common_dev(c, a, unit, state, UC_CHANGE);
738 else
739 printf("Unknown argument\n");
740 break;
741 case 'd':
742 if (*c == '\0')
743 printf("Attr, DevNo or Dev expected\n");
744 else if (userconf_number(c, &a) == 0)
745 userconf_disable(a);
746 else if (userconf_device(c, &a, &unit, &state) == 0)
747 userconf_common_dev(c, a, unit, state, UC_DISABLE);
748 else
749 printf("Unknown argument\n");
750 break;
751 case 'e':
752 if (*c == '\0')
753 printf("Attr, DevNo or Dev expected\n");
754 else if (userconf_number(c, &a) == 0)
755 userconf_enable(a);
756 else if (userconf_device(c, &a, &unit, &state) == 0)
757 userconf_common_dev(c, a, unit, state, UC_ENABLE);
758 else
759 printf("Unknown argument\n");
760 break;
761 case 'f':
762 if (*c == '\0')
763 printf("DevNo or Dev expected\n");
764 else if (userconf_number(c, &a) == 0)
765 userconf_pdev(a);
766 else if (userconf_device(c, &a, &unit, &state) == 0)
767 userconf_common_dev(c, a, unit, state, UC_FIND);
768 else
769 printf("Unknown argument\n");
770 break;
771 case 'h':
772 userconf_help();
773 break;
774 case 'l':
775 if (*c == '\0')
776 userconf_list();
777 else
778 printf("Unknown argument\n");
779 break;
780 case 'q':
781 /* XXX add cmd 'q' eoc */
782 userconf_hist_cmd('q');
783 userconf_hist_eoc();
784 return(-1);
785 case 's':
786 default:
787 printf("Unknown command\n");
788 break;
789 }
790 }
791 return(0);
792 }
793
794 extern void user_config(void);
795
796 void
797 user_config(void)
798 {
799 char prompt[] = "uc> ";
800
801 userconf_init();
802 printf("userconf: configure system autoconfiguration:\n");
803
804 while (1) {
805 printf(prompt);
806 if (getsn(userconf_cmdbuf, sizeof(userconf_cmdbuf)) > 0 &&
807 userconf_parse(userconf_cmdbuf))
808 break;
809 }
810 printf("Continuing...\n");
811 }
812
813 /*
814 * XXX shouldn't this be a common function?
815 */
816 static int
817 getsn(char *cp, int size)
818 {
819 char *lp;
820 int c, len;
821
822 cnpollc(1);
823
824 lp = cp;
825 len = 0;
826 for (;;) {
827 c = cngetc();
828 switch (c) {
829 case '\n':
830 case '\r':
831 printf("\n");
832 *lp++ = '\0';
833 cnpollc(0);
834 return (len);
835 case '\b':
836 case '\177':
837 case '#':
838 if (len) {
839 --len;
840 --lp;
841 printf("\b \b");
842 }
843 continue;
844 case '@':
845 case 'u'&037:
846 len = 0;
847 lp = cp;
848 printf("\n");
849 continue;
850 default:
851 if (len + 1 >= size || c < ' ') {
852 printf("\007");
853 continue;
854 }
855 printf("%c", c);
856 ++len;
857 *lp++ = c;
858 }
859 }
860 }
861