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