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