subr_userconf.c revision 1.15 1 /* $NetBSD: subr_userconf.c,v 1.15 2005/06/23 18:44:44 thorpej 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.15 2005/06/23 18:44:44 thorpej 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 char * const *ln;
210
211 if (devno > userconf_maxdev) {
212 printf("Unknown devno (max is %d)\n", userconf_maxdev);
213 return;
214 }
215
216 cd = &cfdata[devno];
217
218 printf("[%3d] ", devno);
219 userconf_pdevnam(devno);
220 printf(" at");
221 cfp = cd->cf_pspec;
222 if (cfp == NULL)
223 printf(" root");
224 else if (cfp->cfp_parent != NULL && cfp->cfp_unit != -1)
225 printf(" %s%d", cfp->cfp_parent, cfp->cfp_unit);
226 else
227 printf(" %s?", cfp->cfp_parent != NULL ? cfp->cfp_parent
228 : cfp->cfp_iattr);
229 switch (cd->cf_fstate) {
230 case FSTATE_NOTFOUND:
231 case FSTATE_FOUND:
232 case FSTATE_STAR:
233 break;
234 case FSTATE_DNOTFOUND:
235 case FSTATE_DSTAR:
236 printf(" disable");
237 break;
238 default:
239 printf(" ???");
240 break;
241 }
242 l = cd->cf_loc;
243 ln = cd->cf_locnames;
244 while (ln && *ln) {
245 printf(" %s ", *ln++);
246 userconf_pnum(*l++);
247 }
248 printf("\n");
249 }
250
251 static int
252 userconf_number(char *c, int *val)
253 {
254 u_int num = 0;
255 int neg = 0;
256 int base = 10;
257
258 if (*c == '-') {
259 neg = 1;
260 c++;
261 }
262 if (*c == '0') {
263 base = 8;
264 c++;
265 if (*c == 'x' || *c == 'X') {
266 base = 16;
267 c++;
268 }
269 }
270 while (*c != '\n' && *c != '\t' && *c != ' ' && *c != '\0') {
271 u_char cc = *c;
272
273 if (cc >= '0' && cc <= '9')
274 cc = cc - '0';
275 else if (cc >= 'a' && cc <= 'f')
276 cc = cc - 'a' + 10;
277 else if (cc >= 'A' && cc <= 'F')
278 cc = cc - 'A' + 10;
279 else
280 return (-1);
281
282 if (cc > base)
283 return (-1);
284 num = num * base + cc;
285 c++;
286 }
287
288 if (neg && num > INT_MAX) /* overflow */
289 return (1);
290 *val = neg ? - num : num;
291 return (0);
292 }
293
294 static int
295 userconf_device(char *cmd, int *len, short *unit, short *state)
296 {
297 short u = 0, s = FSTATE_FOUND;
298 int l = 0;
299 char *c;
300
301 c = cmd;
302 while (*c >= 'a' && *c <= 'z') {
303 l++;
304 c++;
305 }
306 if (*c == '*') {
307 s = FSTATE_STAR;
308 c++;
309 } else {
310 while (*c >= '0' && *c <= '9') {
311 s = FSTATE_NOTFOUND;
312 u = u*10 + *c - '0';
313 c++;
314 }
315 }
316 while (*c == ' ' || *c == '\t' || *c == '\n')
317 c++;
318
319 if (*c == '\0') {
320 *len = l;
321 *unit = u;
322 *state = s;
323 return(0);
324 }
325
326 return(-1);
327 }
328
329 static void
330 userconf_modify(const char *item, int *val)
331 {
332 int ok = 0;
333 int a;
334 char *c;
335
336 while (!ok) {
337 printf("%s [", item);
338 userconf_pnum(*val);
339 printf("] ? ");
340
341 getsn(userconf_argbuf, sizeof(userconf_argbuf));
342
343 c = userconf_argbuf;
344 while (*c == ' ' || *c == '\t' || *c == '\n') c++;
345
346 if (*c != '\0') {
347 if (userconf_number(c, &a) == 0) {
348 *val = a;
349 ok = 1;
350 } else {
351 printf("Unknown argument\n");
352 }
353 } else {
354 ok = 1;
355 }
356 }
357 }
358
359 static void
360 userconf_change(int devno)
361 {
362 struct cfdata *cd;
363 char c = '\0';
364 int *l;
365 int ln;
366 const char * const *locnames;
367
368 if (devno <= userconf_maxdev) {
369
370 userconf_pdev(devno);
371
372 while (c != 'y' && c != 'Y' && c != 'n' && c != 'N') {
373 printf("change (y/n) ?");
374 c = cngetc();
375 printf("\n");
376 }
377
378 if (c == 'y' || c == 'Y') {
379
380 /* XXX add cmd 'c' <devno> */
381 userconf_hist_cmd('c');
382 userconf_hist_int(devno);
383
384 cd = &cfdata[devno];
385 l = cd->cf_loc;
386 locnames = cd->cf_locnames;
387 ln = 0;
388
389 while (locnames[ln])
390 {
391 userconf_modify(locnames[ln], l);
392
393 /* XXX add *l */
394 userconf_hist_int(*l);
395
396 ln++;
397 l++;
398 }
399
400 printf("[%3d] ", devno);
401 userconf_pdevnam(devno);
402 printf(" changed\n");
403 userconf_pdev(devno);
404
405 /* XXX add eoc */
406 userconf_hist_eoc();
407
408 }
409 } else {
410 printf("Unknown devno (max is %d)\n", userconf_maxdev);
411 }
412 }
413
414 static void
415 userconf_disable(int devno)
416 {
417 int done = 0;
418
419 if (devno <= userconf_maxdev) {
420 switch (cfdata[devno].cf_fstate) {
421 case FSTATE_NOTFOUND:
422 cfdata[devno].cf_fstate = FSTATE_DNOTFOUND;
423 break;
424 case FSTATE_STAR:
425 cfdata[devno].cf_fstate = FSTATE_DSTAR;
426 break;
427 case FSTATE_DNOTFOUND:
428 case FSTATE_DSTAR:
429 done = 1;
430 break;
431 default:
432 printf("Error unknown state\n");
433 break;
434 }
435
436 printf("[%3d] ", devno);
437 userconf_pdevnam(devno);
438 if (done) {
439 printf(" already");
440 } else {
441 /* XXX add cmd 'd' <devno> eoc */
442 userconf_hist_cmd('d');
443 userconf_hist_int(devno);
444 userconf_hist_eoc();
445 }
446 printf(" disabled\n");
447 } else {
448 printf("Unknown devno (max is %d)\n", userconf_maxdev);
449 }
450 }
451
452 static void
453 userconf_enable(int devno)
454 {
455 int done = 0;
456
457 if (devno <= userconf_maxdev) {
458 switch (cfdata[devno].cf_fstate) {
459 case FSTATE_DNOTFOUND:
460 cfdata[devno].cf_fstate = FSTATE_NOTFOUND;
461 break;
462 case FSTATE_DSTAR:
463 cfdata[devno].cf_fstate = FSTATE_STAR;
464 break;
465 case FSTATE_NOTFOUND:
466 case FSTATE_STAR:
467 done = 1;
468 break;
469 default:
470 printf("Error unknown state\n");
471 break;
472 }
473
474 printf("[%3d] ", devno);
475 userconf_pdevnam(devno);
476 if (done) {
477 printf(" already");
478 } else {
479 /* XXX add cmd 'e' <devno> eoc */
480 userconf_hist_cmd('d');
481 userconf_hist_int(devno);
482 userconf_hist_eoc();
483 }
484 printf(" enabled\n");
485 } else {
486 printf("Unknown devno (max is %d)\n", userconf_maxdev);
487 }
488 }
489
490 static void
491 userconf_help(void)
492 {
493 int j = 0, k;
494
495 printf("command args description\n");
496 while (*userconf_cmds[j] != '\0') {
497 printf(userconf_cmds[j]);
498 k = strlen(userconf_cmds[j]);
499 while (k < 10) {
500 printf(" ");
501 k++;
502 }
503 switch (*userconf_cmds[j+1]) {
504 case 'L':
505 printf("[count] number of lines before more");
506 break;
507 case 'b':
508 printf("8|10|16 base on large numbers");
509 break;
510 case 'c':
511 printf("devno|dev change devices");
512 break;
513 case 'd':
514 printf("devno|dev disable devices");
515 break;
516 case 'e':
517 printf("devno|dev enable devices");
518 break;
519 case 'f':
520 printf("devno|dev find devices");
521 break;
522 case 'h':
523 printf(" this message");
524 break;
525 case 'l':
526 printf(" list configuration");
527 break;
528 case 'q':
529 printf(" leave userconf");
530 break;
531 default:
532 printf(" don't know");
533 break;
534 }
535 printf("\n");
536 j += 2;
537 }
538 }
539
540 static void
541 userconf_list(void)
542 {
543 int i = 0;
544
545 userconf_cnt = 0;
546
547 while (cfdata[i].cf_name != NULL) {
548 if (userconf_more())
549 break;
550 userconf_pdev(i++);
551 }
552
553 userconf_cnt = -1;
554 }
555
556 static void
557 userconf_common_dev(char *dev, int len, short unit, short state, char routine)
558 {
559 int i = 0;
560
561 switch (routine) {
562 case UC_CHANGE:
563 break;
564 default:
565 userconf_cnt = 0;
566 break;
567 }
568
569 while (cfdata[i].cf_name != NULL) {
570 if (strlen(cfdata[i].cf_name) == len) {
571
572 /*
573 * Ok, if device name is correct
574 * If state == FSTATE_FOUND, look for "dev"
575 * If state == FSTATE_STAR, look for "dev*"
576 * If state == FSTATE_NOTFOUND, look for "dev0"
577 */
578 if (strncasecmp(dev, cfdata[i].cf_name,
579 len) == 0 &&
580 (state == FSTATE_FOUND ||
581 (state == FSTATE_STAR &&
582 (cfdata[i].cf_fstate == FSTATE_STAR ||
583 cfdata[i].cf_fstate == FSTATE_DSTAR)) ||
584 (state == FSTATE_NOTFOUND &&
585 cfdata[i].cf_unit == unit &&
586 (cfdata[i].cf_fstate == FSTATE_NOTFOUND ||
587 cfdata[i].cf_fstate == FSTATE_DNOTFOUND)))) {
588 if (userconf_more())
589 break;
590 switch (routine) {
591 case UC_CHANGE:
592 userconf_change(i);
593 break;
594 case UC_ENABLE:
595 userconf_enable(i);
596 break;
597 case UC_DISABLE:
598 userconf_disable(i);
599 break;
600 case UC_FIND:
601 userconf_pdev(i);
602 break;
603 default:
604 printf("Unknown routine /%c/\n",
605 routine);
606 break;
607 }
608 }
609 }
610 i++;
611 }
612
613 switch (routine) {
614 case UC_CHANGE:
615 break;
616 default:
617 userconf_cnt = -1;
618 break;
619 }
620 }
621
622 #if 0
623 static void
624 userconf_add_read(char *prompt, char field, char *dev, int len, int *val)
625 {
626 int ok = 0;
627 int a;
628 char *c;
629
630 *val = -1;
631
632 while (!ok) {
633 printf("%s ? ", prompt);
634
635 getsn(userconf_argbuf, sizeof(userconf_argbuf));
636
637 c = userconf_argbuf;
638 while (*c == ' ' || *c == '\t' || *c == '\n') c++;
639
640 if (*c != '\0') {
641 if (userconf_number(c, &a) == 0) {
642 if (a > userconf_maxdev) {
643 printf("Unknown devno (max is %d)\n",
644 userconf_maxdev);
645 } else if (strncasecmp(dev,
646 cfdata[a].cf_name, len) != 0 &&
647 field == 'a') {
648 printf("Not same device type\n");
649 } else {
650 *val = a;
651 ok = 1;
652 }
653 } else if (*c == '?') {
654 userconf_common_dev(dev, len, 0,
655 FSTATE_FOUND, UC_FIND);
656 } else if (*c == 'q' || *c == 'Q') {
657 ok = 1;
658 } else {
659 printf("Unknown argument\n");
660 }
661 } else {
662 ok = 1;
663 }
664 }
665 }
666 #endif /* 0 */
667
668 static int
669 userconf_parse(char *cmd)
670 {
671 char *c, *v;
672 int i = 0, j = 0, k, a;
673 short unit, state;
674
675 c = cmd;
676 while (*c == ' ' || *c == '\t')
677 c++;
678 v = c;
679 while (*c != ' ' && *c != '\t' && *c != '\n' && *c != '\0') {
680 c++;
681 i++;
682 }
683
684 k = -1;
685 while (*userconf_cmds[j] != '\0') {
686 if (strlen(userconf_cmds[j]) == i) {
687 if (strncasecmp(v, userconf_cmds[j], i) == 0)
688 k = j;
689 }
690 j += 2;
691 }
692
693 while (*c == ' ' || *c == '\t' || *c == '\n')
694 c++;
695
696 if (k == -1) {
697 if (*v != '\n')
698 printf("Unknown command, try help\n");
699 } else {
700 switch (*userconf_cmds[k+1]) {
701 case 'L':
702 if (*c == '\0')
703 printf("Argument expected\n");
704 else if (userconf_number(c, &a) == 0)
705 userconf_lines = a;
706 else
707 printf("Unknown argument\n");
708 break;
709 case 'b':
710 if (*c == '\0')
711 printf("8|10|16 expected\n");
712 else if (userconf_number(c, &a) == 0) {
713 if (a == 8 || a == 10 || a == 16) {
714 userconf_base = a;
715 } else {
716 printf("8|10|16 expected\n");
717 }
718 } else
719 printf("Unknown argument\n");
720 break;
721 case 'c':
722 if (*c == '\0')
723 printf("DevNo or Dev expected\n");
724 else if (userconf_number(c, &a) == 0)
725 userconf_change(a);
726 else if (userconf_device(c, &a, &unit, &state) == 0)
727 userconf_common_dev(c, a, unit, state, UC_CHANGE);
728 else
729 printf("Unknown argument\n");
730 break;
731 case 'd':
732 if (*c == '\0')
733 printf("Attr, DevNo or Dev expected\n");
734 else if (userconf_number(c, &a) == 0)
735 userconf_disable(a);
736 else if (userconf_device(c, &a, &unit, &state) == 0)
737 userconf_common_dev(c, a, unit, state, UC_DISABLE);
738 else
739 printf("Unknown argument\n");
740 break;
741 case 'e':
742 if (*c == '\0')
743 printf("Attr, DevNo or Dev expected\n");
744 else if (userconf_number(c, &a) == 0)
745 userconf_enable(a);
746 else if (userconf_device(c, &a, &unit, &state) == 0)
747 userconf_common_dev(c, a, unit, state, UC_ENABLE);
748 else
749 printf("Unknown argument\n");
750 break;
751 case 'f':
752 if (*c == '\0')
753 printf("DevNo or Dev expected\n");
754 else if (userconf_number(c, &a) == 0)
755 userconf_pdev(a);
756 else if (userconf_device(c, &a, &unit, &state) == 0)
757 userconf_common_dev(c, a, unit, state, UC_FIND);
758 else
759 printf("Unknown argument\n");
760 break;
761 case 'h':
762 userconf_help();
763 break;
764 case 'l':
765 if (*c == '\0')
766 userconf_list();
767 else
768 printf("Unknown argument\n");
769 break;
770 case 'q':
771 /* XXX add cmd 'q' eoc */
772 userconf_hist_cmd('q');
773 userconf_hist_eoc();
774 return(-1);
775 case 's':
776 default:
777 printf("Unknown command\n");
778 break;
779 }
780 }
781 return(0);
782 }
783
784 extern void user_config(void);
785
786 void
787 user_config(void)
788 {
789 char prompt[] = "uc> ";
790
791 userconf_init();
792 printf("userconf: configure system autoconfiguration:\n");
793
794 while (1) {
795 printf(prompt);
796 if (getsn(userconf_cmdbuf, sizeof(userconf_cmdbuf)) > 0 &&
797 userconf_parse(userconf_cmdbuf))
798 break;
799 }
800 printf("Continuing...\n");
801 }
802
803 /*
804 * XXX shouldn't this be a common function?
805 */
806 static int
807 getsn(char *cp, int size)
808 {
809 char *lp;
810 int c, len;
811
812 cnpollc(1);
813
814 lp = cp;
815 len = 0;
816 for (;;) {
817 c = cngetc();
818 switch (c) {
819 case '\n':
820 case '\r':
821 printf("\n");
822 *lp++ = '\0';
823 cnpollc(0);
824 return (len);
825 case '\b':
826 case '\177':
827 case '#':
828 if (len) {
829 --len;
830 --lp;
831 printf("\b \b");
832 }
833 continue;
834 case '@':
835 case 'u'&037:
836 len = 0;
837 lp = cp;
838 printf("\n");
839 continue;
840 default:
841 if (len + 1 >= size || c < ' ') {
842 printf("\007");
843 continue;
844 }
845 printf("%c", c);
846 ++len;
847 *lp++ = c;
848 }
849 }
850 }
851