xref: /openbsd-src/sys/kern/subr_userconf.c (revision e5157e49389faebcb42b7237d55fbf096d9c2523)
1 /*	$OpenBSD: subr_userconf.c,v 1.43 2014/11/03 03:08:00 deraadt Exp $	*/
2 
3 /*
4  * Copyright (c) 1996-2001 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 
29 #include <sys/param.h>
30 #include <sys/systm.h>
31 #include <sys/device.h>
32 #include <sys/malloc.h>
33 #include <sys/time.h>
34 
35 #include <dev/cons.h>
36 
37 extern char *locnames[];
38 extern short locnamp[];
39 extern short cfroots[];
40 extern int cfroots_size;
41 extern int pv_size;
42 extern short pv[];
43 extern struct timezone tz;
44 extern char *pdevnames[];
45 extern int pdevnames_size;
46 extern struct pdevinit pdevinit[];
47 
48 int userconf_base = 16;				/* Base for "large" numbers */
49 int userconf_maxdev = -1;			/* # of used device slots   */
50 int userconf_totdev = -1;			/* # of device slots        */
51 int userconf_maxlocnames = -1;			/* # of locnames            */
52 int userconf_cnt = -1;				/* Line counter for ...     */
53 int userconf_lines = 12;			/* ... # of lines per page  */
54 int userconf_histlen = 0;
55 int userconf_histcur = 0;
56 char userconf_history[1024];
57 int userconf_histsz = sizeof(userconf_history);
58 char userconf_argbuf[40];			/* Additional input         */
59 char userconf_cmdbuf[40];			/* Command line             */
60 char userconf_histbuf[40];
61 
62 void userconf_init(void);
63 int userconf_more(void);
64 void userconf_modify(char *, int *);
65 void userconf_hist_cmd(char);
66 void userconf_hist_int(int);
67 void userconf_hist_eoc(void);
68 void userconf_pnum(int);
69 void userconf_pdevnam(short);
70 void userconf_pdev(short);
71 int userconf_number(char *, int *);
72 int userconf_device(char *, int *, short *, short *);
73 int userconf_attr(char *, int *);
74 void userconf_modify(char *, int *);
75 void userconf_change(int);
76 void userconf_disable(int);
77 void userconf_enable(int);
78 void userconf_help(void);
79 void userconf_list(void);
80 void userconf_show(void);
81 void userconf_common_attr_val(short, int *, char);
82 void userconf_show_attr(char *);
83 void userconf_common_dev(char *, int, short, short, char);
84 void userconf_common_attr(char *, int, char);
85 void userconf_add_read(char *, char, char *, int, int *);
86 void userconf_add(char *, int, short, short);
87 int userconf_parse(char *);
88 
89 #define UC_CHANGE 'c'
90 #define UC_DISABLE 'd'
91 #define UC_ENABLE 'e'
92 #define UC_FIND 'f'
93 #define UC_SHOW 's'
94 
95 char *userconf_cmds[] = {
96 	"add",		"a",
97 	"base",		"b",
98 	"change",	"c",
99 #if defined(DDB)
100 	"ddb",		"D",
101 #endif
102 	"disable",	"d",
103 	"enable",	"e",
104 	"exit",		"q",
105 	"find",		"f",
106 	"help",		"h",
107 	"list",		"l",
108 	"lines",	"L",
109 	"quit",		"q",
110 	"show",		"s",
111 	"timezone",	"t",
112 	"verbose",	"v",
113 	"?",		"h",
114 	"",		 "",
115 };
116 
117 void
118 userconf_init(void)
119 {
120 	int i = 0;
121 	struct cfdata *cd;
122 	int   ln;
123 
124 	while (cfdata[i].cf_attach != 0) {
125 		userconf_maxdev = i;
126 		userconf_totdev = i;
127 
128 		cd = &cfdata[i];
129 		ln = cd->cf_locnames;
130 		while (locnamp[ln] != -1) {
131 			if (locnamp[ln] > userconf_maxlocnames)
132 				userconf_maxlocnames = locnamp[ln];
133 			ln++;
134 		}
135 		i++;
136 	}
137 
138 	while (cfdata[i].cf_attach == 0) {
139 		userconf_totdev = i;
140 		i++;
141 	}
142 	userconf_totdev = userconf_totdev - 1;
143 }
144 
145 int
146 userconf_more(void)
147 {
148 	int quit = 0;
149 	char c = '\0';
150 
151 	if (userconf_cnt != -1) {
152 		if (userconf_cnt == userconf_lines) {
153 			printf("--- more ---");
154 			c = cngetc();
155 			userconf_cnt = 0;
156 			printf("\r            \r");
157 		}
158 		userconf_cnt++;
159 		if (c == 'q' || c == 'Q')
160 			quit = 1;
161 	}
162 	return (quit);
163 }
164 
165 void
166 userconf_hist_cmd(char cmd)
167 {
168 	userconf_histcur = userconf_histlen;
169 	if (userconf_histcur < userconf_histsz) {
170 		userconf_history[userconf_histcur] = cmd;
171 		userconf_histcur++;
172 	}
173 }
174 
175 void
176 userconf_hist_int(int val)
177 {
178 	snprintf(userconf_histbuf, sizeof userconf_histbuf, " %d",val);
179 	if (userconf_histcur + strlen(userconf_histbuf) < userconf_histsz) {
180 		bcopy(userconf_histbuf,
181 		    &userconf_history[userconf_histcur],
182 		    strlen(userconf_histbuf));
183 		userconf_histcur = userconf_histcur + strlen(userconf_histbuf);
184 	}
185 }
186 
187 void
188 userconf_hist_eoc(void)
189 {
190 	if (userconf_histcur < userconf_histsz) {
191 		userconf_history[userconf_histcur] = '\n';
192 		userconf_histcur++;
193 		userconf_histlen = userconf_histcur;
194 	}
195 }
196 
197 void
198 userconf_pnum(int val)
199 {
200 	if (val > -2 && val < 16) {
201 		printf("%d",val);
202 		return;
203 	}
204 
205 	switch (userconf_base) {
206 	case 8:
207 		printf("0%o",val);
208 		break;
209 	case 10:
210 		printf("%d",val);
211 		break;
212 	case 16:
213 	default:
214 		printf("0x%x",val);
215 		break;
216 	}
217 }
218 
219 void
220 userconf_pdevnam(short dev)
221 {
222 	struct cfdata *cd;
223 
224 	cd = &cfdata[dev];
225 	printf("%s", cd->cf_driver->cd_name);
226 	switch (cd->cf_fstate) {
227 	case FSTATE_NOTFOUND:
228 	case FSTATE_DNOTFOUND:
229 		printf("%d", cd->cf_unit);
230 		break;
231 	case FSTATE_FOUND:
232 		printf("*FOUND*");
233 		break;
234 	case FSTATE_STAR:
235 	case FSTATE_DSTAR:
236 		printf("*");
237 		break;
238 	default:
239 		printf("*UNKNOWN*");
240 		break;
241 	}
242 }
243 
244 void
245 userconf_pdev(short devno)
246 {
247 	struct cfdata *cd;
248 	short *p;
249 	int   *l;
250 	int   ln;
251 	char c;
252 
253 	if (devno > userconf_maxdev && devno <= userconf_totdev) {
254 		printf("%3d free slot (for add)\n", devno);
255 		return;
256 	}
257 
258 	if (devno > userconf_totdev &&
259 	    devno <= userconf_totdev+pdevnames_size) {
260 		printf("%3d %s count %d", devno,
261 		    pdevnames[devno-userconf_totdev-1],
262 		    abs(pdevinit[devno-userconf_totdev-1].pdev_count));
263 		if (pdevinit[devno-userconf_totdev-1].pdev_count < 1)
264 			printf(" disable");
265 		printf(" (pseudo device)\n");
266 		return;
267 	}
268 
269 	if (devno >  userconf_maxdev) {
270 		printf("Unknown devno (max is %d)\n", userconf_maxdev);
271 		return;
272 	}
273 
274 	cd = &cfdata[devno];
275 
276 	printf("%3d ", devno);
277 	userconf_pdevnam(devno);
278 	printf(" at");
279 	c = ' ';
280 	p = cd->cf_parents;
281 	if (*p == -1)
282 		printf(" root");
283 	while (*p != -1) {
284 		printf("%c", c);
285 		userconf_pdevnam(*p++);
286 		c = '|';
287 	}
288 	switch (cd->cf_fstate) {
289 	case FSTATE_NOTFOUND:
290 	case FSTATE_FOUND:
291 	case FSTATE_STAR:
292 		break;
293 	case FSTATE_DNOTFOUND:
294 	case FSTATE_DSTAR:
295 		printf(" disable");
296 		break;
297 	default:
298 		printf(" ???");
299 		break;
300 	}
301 	l = cd->cf_loc;
302 	ln = cd->cf_locnames;
303 	while (locnamp[ln] != -1) {
304 		printf(" %s ", locnames[locnamp[ln]]);
305 		ln++;
306 		userconf_pnum(*l++);
307 	}
308 	printf(" flags 0x%x\n", cd->cf_flags);
309 }
310 
311 int
312 userconf_number(char *c, int *val)
313 {
314 	u_int num = 0;
315 	int neg = 0;
316 	int base = 10;
317 
318 	if (*c == '-') {
319 		neg = 1;
320 		c++;
321 	}
322 	if (*c == '0') {
323 		base = 8;
324 		c++;
325 		if (*c == 'x' || *c == 'X') {
326 			base = 16;
327 			c++;
328 		}
329 	}
330 	while (*c != '\n' && *c != '\t' && *c != ' ' && *c != '\0') {
331 		u_char cc = *c;
332 
333 		if (cc >= '0' && cc <= '9')
334 			cc = cc - '0';
335 		else if (cc >= 'a' && cc <= 'f')
336 			cc = cc - 'a' + 10;
337 		else if (cc >= 'A' && cc <= 'F')
338 			cc = cc - 'A' + 10;
339 		else
340 			return (-1);
341 
342 		if (cc > base)
343 			return (-1);
344 		num = num * base + cc;
345 		c++;
346 	}
347 
348 	if (neg && num > INT_MAX)	/* overflow */
349 		return (1);
350 	*val = neg ? - num : num;
351 	return (0);
352 }
353 
354 int
355 userconf_device(char *cmd, int *len, short *unit, short *state)
356 {
357 	short u = 0, s = FSTATE_FOUND;
358 	int l = 0;
359 	char *c;
360 
361 	c = cmd;
362 	while (*c >= 'a' && *c <= 'z') {
363 		l++;
364 		c++;
365 	}
366 	if (*c == '*') {
367 		s = FSTATE_STAR;
368 		c++;
369 	} else {
370 		while (*c >= '0' && *c <= '9') {
371 			s = FSTATE_NOTFOUND;
372 			u = u*10 + *c - '0';
373 			c++;
374 		}
375 	}
376 	while (*c == ' ' || *c == '\t' || *c == '\n')
377 		c++;
378 
379 	if (*c == '\0') {
380 		*len = l;
381 		*unit = u;
382 		*state = s;
383 		return(0);
384 	}
385 
386 	return(-1);
387 }
388 
389 int
390 userconf_attr(char *cmd, int *val)
391 {
392 	char *c;
393 	short attr = -1, i = 0, l = 0;
394 
395 	c = cmd;
396 	while (*c != ' ' && *c != '\t' && *c != '\n' && *c != '\0') {
397 		c++;
398 		l++;
399 	}
400 
401 	while (i <= userconf_maxlocnames) {
402 		if (strlen(locnames[i]) == l) {
403 			if (strncasecmp(cmd, locnames[i], l) == 0)
404 				attr = i;
405 		}
406 		i++;
407 	}
408 
409 	if (attr == -1) {
410 		return (-1);
411 	}
412 
413 	*val = attr;
414 
415 	return(0);
416 }
417 
418 void
419 userconf_modify(char *item, int *val)
420 {
421 	int ok = 0;
422 	int a;
423 	char *c;
424 	int i;
425 
426 	while (!ok) {
427 		printf("%s [", item);
428 		userconf_pnum(*val);
429 		printf("] ? ");
430 
431 		i = getsn(userconf_argbuf, sizeof(userconf_argbuf));
432 
433 		c = userconf_argbuf;
434 		while (*c == ' ' || *c == '\t' || *c == '\n') c++;
435 
436 		if (*c != '\0') {
437 			if (userconf_number(c, &a) == 0) {
438 				*val = a;
439 				ok = 1;
440 			} else {
441 				printf("Unknown argument\n");
442 			}
443 		} else {
444 			ok = 1;
445 		}
446 	}
447 }
448 
449 void
450 userconf_change(int devno)
451 {
452 	struct cfdata *cd;
453 	char c = '\0';
454 	int   *l;
455 	int   ln;
456 
457 	if (devno <=  userconf_maxdev) {
458 		userconf_pdev(devno);
459 
460 		while (c != 'y' && c != 'Y' && c != 'n' && c != 'N') {
461 			printf("change (y/n) ?");
462 			c = cngetc();
463 			printf("\n");
464 		}
465 
466 		if (c == 'y' || c == 'Y') {
467 			int share = 0, i, *lk, lklen;
468 
469 			/* XXX add cmd 'c' <devno> */
470 			userconf_hist_cmd('c');
471 			userconf_hist_int(devno);
472 
473 			cd = &cfdata[devno];
474 			l = cd->cf_loc;
475 			ln = cd->cf_locnames;
476 
477 			/*
478 			 * Search for some other driver sharing this
479 			 * locator table. if one does, we may need to
480 			 * replace the locators with a malloc'd copy.
481 			 */
482 			for (i = 0; cfdata[i].cf_driver; i++)
483 				if (i != devno && cfdata[i].cf_loc == l)
484 					share = 1;
485 			if (share) {
486 				for (i = 0; locnamp[ln+i] != -1 ; i++)
487 					;
488 				lk = l = mallocarray(i, sizeof(int),
489 				    M_TEMP, M_NOWAIT);
490 				if (lk == NULL) {
491 					printf("out of memory.\n");
492 					return;
493 				}
494 				lklen = i * sizeof(int);
495 				bcopy(cd->cf_loc, l, lklen);
496 			}
497 
498 			while (locnamp[ln] != -1) {
499 				userconf_modify(locnames[locnamp[ln]], l);
500 
501 				/* XXX add *l */
502 				userconf_hist_int(*l);
503 
504 				ln++;
505 				l++;
506 			}
507 			userconf_modify("flags", &cd->cf_flags);
508 			userconf_hist_int(cd->cf_flags);
509 
510 			if (share) {
511 				if (memcmp(cd->cf_loc, lk, lklen))
512 					cd->cf_loc = lk;
513 				else
514 					free(lk, M_TEMP, lklen);
515 			}
516 
517 			printf("%3d ", devno);
518 			userconf_pdevnam(devno);
519 			printf(" changed\n");
520 			userconf_pdev(devno);
521 		}
522 		return;
523 	}
524 
525 	if (devno > userconf_maxdev && devno <= userconf_totdev) {
526 		printf("%3d can't change free slot\n", devno);
527 		return;
528 	}
529 
530 	if (devno > userconf_totdev &&
531 	    devno <= userconf_totdev+pdevnames_size) {
532 		userconf_pdev(devno);
533 		while (c != 'y' && c != 'Y' && c != 'n' && c != 'N') {
534 			printf("change (y/n) ?");
535 			c = cngetc();
536 			printf("\n");
537 		}
538 
539 		if (c == 'y' || c == 'Y') {
540 			/* XXX add cmd 'c' <devno> */
541 			userconf_hist_cmd('c');
542 			userconf_hist_int(devno);
543 
544 			userconf_modify("count",
545 			    &pdevinit[devno-userconf_totdev-1].pdev_count);
546 			userconf_hist_int(pdevinit[devno-userconf_totdev-1].pdev_count);
547 
548 			printf("%3d %s changed\n", devno,
549 			    pdevnames[devno-userconf_totdev-1]);
550 			userconf_pdev(devno);
551 
552 			/* XXX add eoc */
553 			userconf_hist_eoc();
554 		}
555 		return;
556 	}
557 
558 	printf("Unknown devno (max is %d)\n", userconf_totdev+pdevnames_size);
559 }
560 
561 void
562 userconf_disable(int devno)
563 {
564 	int done = 0;
565 
566 	if (devno <= userconf_maxdev) {
567 		switch (cfdata[devno].cf_fstate) {
568 		case FSTATE_NOTFOUND:
569 			cfdata[devno].cf_fstate = FSTATE_DNOTFOUND;
570 			break;
571 		case FSTATE_STAR:
572 			cfdata[devno].cf_fstate = FSTATE_DSTAR;
573 			break;
574 		case FSTATE_DNOTFOUND:
575 		case FSTATE_DSTAR:
576 			done = 1;
577 			break;
578 		default:
579 			printf("Error unknown state\n");
580 			break;
581 		}
582 
583 		printf("%3d ", devno);
584 		userconf_pdevnam(devno);
585 		if (done) {
586 			printf(" already");
587 		} else {
588 			/* XXX add cmd 'd' <devno> eoc */
589 			userconf_hist_cmd('d');
590 			userconf_hist_int(devno);
591 			userconf_hist_eoc();
592 		}
593 		printf(" disabled\n");
594 
595 		return;
596 	}
597 
598 	if (devno > userconf_maxdev && devno <= userconf_totdev) {
599 		printf("%3d can't disable free slot\n", devno);
600 		return;
601 	}
602 
603 	if (devno > userconf_totdev &&
604 	    devno <= userconf_totdev+pdevnames_size) {
605 		printf("%3d %s", devno, pdevnames[devno-userconf_totdev-1]);
606 		if (pdevinit[devno-userconf_totdev-1].pdev_count < 1) {
607 			printf(" already ");
608 		} else {
609 			pdevinit[devno-userconf_totdev-1].pdev_count *= -1;
610 			/* XXX add cmd 'd' <devno> eoc */
611 			userconf_hist_cmd('d');
612 			userconf_hist_int(devno);
613 			userconf_hist_eoc();
614 		}
615 		printf(" disabled\n");
616 		return;
617 	}
618 
619 	printf("Unknown devno (max is %d)\n", userconf_totdev+pdevnames_size);
620 }
621 
622 void
623 userconf_enable(int devno)
624 {
625 	int done = 0;
626 
627 	if (devno <= userconf_maxdev) {
628 		switch (cfdata[devno].cf_fstate) {
629 		case FSTATE_DNOTFOUND:
630 			cfdata[devno].cf_fstate = FSTATE_NOTFOUND;
631 			break;
632 		case FSTATE_DSTAR:
633 			cfdata[devno].cf_fstate = FSTATE_STAR;
634 			break;
635 		case FSTATE_NOTFOUND:
636 		case FSTATE_STAR:
637 			done = 1;
638 			break;
639 		default:
640 			printf("Error unknown state\n");
641 			break;
642 		}
643 
644 		printf("%3d ", devno);
645 		userconf_pdevnam(devno);
646 		if (done) {
647 			printf(" already");
648 		} else {
649 			/* XXX add cmd 'e' <devno> eoc */
650 			userconf_hist_cmd('e');
651 			userconf_hist_int(devno);
652 			userconf_hist_eoc();
653 		}
654 		printf(" enabled\n");
655 		return;
656 	}
657 
658 	if (devno > userconf_maxdev && devno <= userconf_totdev) {
659 		printf("%3d can't enable free slot\n", devno);
660 		return;
661 	}
662 
663 	if (devno > userconf_totdev &&
664 	    devno <= userconf_totdev+pdevnames_size) {
665 		printf("%3d %s", devno, pdevnames[devno-userconf_totdev-1]);
666 		if (pdevinit[devno-userconf_totdev-1].pdev_count > 0) {
667 			printf(" already");
668 		} else {
669 			pdevinit[devno-userconf_totdev-1].pdev_count *= -1;
670 			/* XXX add cmd 'e' <devno> eoc */
671 			userconf_hist_cmd('e');
672 			userconf_hist_int(devno);
673 			userconf_hist_eoc();
674 		}
675 		printf(" enabled\n");
676 		return;
677 	}
678 
679 	printf("Unknown devno (max is %d)\n", userconf_totdev+pdevnames_size);
680 }
681 
682 void
683 userconf_help(void)
684 {
685 	int j = 0, k;
686 
687 	printf("command   args                description\n");
688 	while (*userconf_cmds[j] != '\0') {
689 		printf("%s", userconf_cmds[j]);
690 		k = strlen(userconf_cmds[j]);
691 		while (k < 10) {
692 			printf(" ");
693 			k++;
694 		}
695 		switch (*userconf_cmds[j+1]) {
696 		case 'L':
697 			printf("[count]             number of lines before more");
698 			break;
699 		case 'a':
700 			printf("dev                 add a device");
701 			break;
702 		case 'b':
703 			printf("8|10|16             base on large numbers");
704 			break;
705 		case 'c':
706 			printf("devno|dev           change devices");
707 			break;
708 #if defined(DDB)
709 		case 'D':
710 			printf("                    enter ddb");
711 			break;
712 #endif
713 		case 'd':
714 			printf("attr val|devno|dev  disable devices");
715 			break;
716 		case 'e':
717 			printf("attr val|devno|dev  enable devices");
718 			break;
719 		case 'f':
720 			printf("devno|dev           find devices");
721 			break;
722 		case 'h':
723 			printf("                    this message");
724 			break;
725 		case 'l':
726 			printf("                    list configuration");
727 			break;
728 		case 'q':
729 			printf("                    leave UKC");
730 			break;
731 		case 's':
732 			printf("[attr [val]]        "
733 			   "show attributes (or devices with an attribute)");
734 			break;
735 		case 't':
736 			printf("[mins [dst]]        set timezone/dst");
737 			break;
738 		case 'v':
739 			printf("                    toggle verbose booting");
740 			break;
741 		default:
742 			printf("                    don't know");
743 			break;
744 		}
745 		printf("\n");
746 		j += 2;
747 	}
748 }
749 
750 void
751 userconf_list(void)
752 {
753 	int i = 0;
754 
755 	userconf_cnt = 0;
756 
757 	while (i <= (userconf_totdev+pdevnames_size)) {
758 		if (userconf_more())
759 			break;
760 		userconf_pdev(i++);
761 	}
762 
763 	userconf_cnt = -1;
764 }
765 
766 void
767 userconf_show(void)
768 {
769 	int i = 0;
770 
771 	userconf_cnt = 0;
772 
773 	while (i <= userconf_maxlocnames) {
774 		if (userconf_more())
775 			break;
776 		printf("%s\n", locnames[i++]);
777 	}
778 
779 	userconf_cnt = -1;
780 }
781 
782 void
783 userconf_common_attr_val(short attr, int *val, char routine)
784 {
785 	struct cfdata *cd;
786 	int   *l;
787 	int   ln;
788 	int i = 0, quit = 0;
789 
790 	userconf_cnt = 0;
791 
792 	while (i <= userconf_maxdev) {
793 		cd = &cfdata[i];
794 		l = cd->cf_loc;
795 		ln = cd->cf_locnames;
796 		while (locnamp[ln] != -1) {
797 			if (locnamp[ln] == attr) {
798 				if (val == NULL) {
799 					quit = userconf_more();
800 					userconf_pdev(i);
801 				} else {
802 					if (*val == *l) {
803 						quit = userconf_more();
804 						switch (routine) {
805 						case UC_ENABLE:
806 							userconf_enable(i);
807 							break;
808 						case UC_DISABLE:
809 							userconf_disable(i);
810 							break;
811 						case UC_SHOW:
812 							userconf_pdev(i);
813 							break;
814 						default:
815 							printf("Unknown routine /%c/\n",
816 							    routine);
817 							break;
818 						}
819 					}
820 				}
821 			}
822 			if (quit)
823 				break;
824 			ln++;
825 			l++;
826 		}
827 		if (quit)
828 			break;
829 		i++;
830 	}
831 
832 	userconf_cnt = -1;
833 }
834 
835 void
836 userconf_show_attr(char *cmd)
837 {
838 	char *c;
839 	short attr = -1, i = 0, l = 0;
840 	int a;
841 
842 	c = cmd;
843 	while (*c != ' ' && *c != '\t' && *c != '\n' && *c != '\0') {
844 		c++;
845 		l++;
846 	}
847 	while (*c == ' ' || *c == '\t' || *c == '\n') {
848 		c++;
849 	}
850 	while (i <= userconf_maxlocnames) {
851 		if (strlen(locnames[i]) == l) {
852 			if (strncasecmp(cmd, locnames[i], l) == 0) {
853 				attr = i;
854 			}
855 		}
856 		i++;
857 	}
858 
859 	if (attr == -1) {
860 		printf("Unknown attribute\n");
861 		return;
862 	}
863 
864 	if (*c == '\0') {
865 		userconf_common_attr_val(attr, NULL, UC_SHOW);
866 	} else {
867 		if (userconf_number(c, &a) == 0) {
868 			userconf_common_attr_val(attr, &a, UC_SHOW);
869 		} else {
870 			printf("Unknown argument\n");
871 		}
872 	}
873 }
874 
875 void
876 userconf_common_dev(char *dev, int len, short unit, short state, char routine)
877 {
878 	int i = 0;
879 
880 	switch (routine) {
881 	case UC_CHANGE:
882 		break;
883 	default:
884 		userconf_cnt = 0;
885 		break;
886 	}
887 
888 	while (cfdata[i].cf_attach != 0) {
889 		if (strlen(cfdata[i].cf_driver->cd_name) == len) {
890 
891 			/*
892 			 * Ok, if device name is correct
893 			 *  If state == FSTATE_FOUND, look for "dev"
894 			 *  If state == FSTATE_STAR, look for "dev*"
895 			 *  If state == FSTATE_NOTFOUND, look for "dev0"
896 			 */
897 			if (strncasecmp(dev, cfdata[i].cf_driver->cd_name,
898 					len) == 0 &&
899 			    (state == FSTATE_FOUND ||
900 			     (state == FSTATE_STAR &&
901 			      (cfdata[i].cf_fstate == FSTATE_STAR ||
902 			       cfdata[i].cf_fstate == FSTATE_DSTAR)) ||
903 			     (state == FSTATE_NOTFOUND &&
904 			      cfdata[i].cf_unit == unit &&
905 			      (cfdata[i].cf_fstate == FSTATE_NOTFOUND ||
906 			       cfdata[i].cf_fstate == FSTATE_DNOTFOUND)))) {
907 				if (userconf_more())
908 					break;
909 				switch (routine) {
910 				case UC_CHANGE:
911 					userconf_change(i);
912 					break;
913 				case UC_ENABLE:
914 					userconf_enable(i);
915 					break;
916 				case UC_DISABLE:
917 					userconf_disable(i);
918 					break;
919 				case UC_FIND:
920 					userconf_pdev(i);
921 					break;
922 				default:
923 					printf("Unknown routine /%c/\n",
924 					    routine);
925 					break;
926 				}
927 			}
928 		}
929 		i++;
930 	}
931 
932 	for (i = 0; i < pdevnames_size; i++) {
933 		if (strncasecmp(dev, pdevnames[i], len) == 0 &&
934 		    state == FSTATE_FOUND) {
935 			switch(routine) {
936 			case UC_CHANGE:
937 				userconf_change(userconf_totdev+1+i);
938 				break;
939 			case UC_ENABLE:
940 				userconf_enable(userconf_totdev+1+i);
941 				break;
942 			case UC_DISABLE:
943 				userconf_disable(userconf_totdev+1+i);
944 				break;
945 			case UC_FIND:
946 				userconf_pdev(userconf_totdev+1+i);
947 				break;
948 			default:
949 				printf("Unknown pseudo routine /%c/\n",routine);
950 				break;
951 			}
952 		}
953 	}
954 
955 	switch (routine) {
956 	case UC_CHANGE:
957 		break;
958 	default:
959 		userconf_cnt = -1;
960 		break;
961 	}
962 }
963 
964 void
965 userconf_common_attr(char *cmd, int attr, char routine)
966 {
967 	char *c;
968 	short l = 0;
969 	int a;
970 
971 	c = cmd;
972 	while (*c != ' ' && *c != '\t' && *c != '\n' && *c != '\0') {
973 		c++;
974 		l++;
975 	}
976 	while (*c == ' ' || *c == '\t' || *c == '\n')
977 		c++;
978 
979 	if (*c == '\0') {
980 		printf("Value missing for attribute\n");
981 		return;
982 	}
983 
984 	if (userconf_number(c, &a) == 0) {
985 		userconf_common_attr_val(attr, &a, routine);
986 	} else {
987 		printf("Unknown argument\n");
988 	}
989 }
990 
991 void
992 userconf_add_read(char *prompt, char field, char *dev, int len, int *val)
993 {
994 	int ok = 0;
995 	int a;
996 	char *c;
997 	int i;
998 
999 	*val = -1;
1000 
1001 	while (!ok) {
1002 		printf("%s ? ", prompt);
1003 
1004 		i = getsn(userconf_argbuf, sizeof(userconf_argbuf));
1005 
1006 		c = userconf_argbuf;
1007 		while (*c == ' ' || *c == '\t' || *c == '\n')
1008 			c++;
1009 
1010 		if (*c != '\0') {
1011 			if (userconf_number(c, &a) == 0) {
1012 				if (a > userconf_maxdev) {
1013 					printf("Unknown devno (max is %d)\n",
1014 					    userconf_maxdev);
1015 				} else if (strncasecmp(dev,
1016 				    cfdata[a].cf_driver->cd_name, len) != 0 &&
1017 				    field == 'a') {
1018 					printf("Not same device type\n");
1019 				} else {
1020 					*val = a;
1021 					ok = 1;
1022 				}
1023 			} else if (*c == '?') {
1024 				userconf_common_dev(dev, len, 0,
1025 				    FSTATE_FOUND, UC_FIND);
1026 			} else if (*c == 'q' || *c == 'Q') {
1027 				ok = 1;
1028 			} else {
1029 				printf("Unknown argument\n");
1030 			}
1031 		} else {
1032 			ok = 1;
1033 		}
1034 	}
1035 }
1036 
1037 void
1038 userconf_add(char *dev, int len, short unit, short state)
1039 {
1040 	int i = 0, found = 0;
1041 	struct cfdata new;
1042 	int  val, max_unit, star_unit, orig;
1043 
1044 	memset(&new, 0, sizeof(struct cfdata));
1045 
1046 	if (userconf_maxdev == userconf_totdev) {
1047 		printf("No more space for new devices.\n");
1048 		return;
1049 	}
1050 
1051 	if (state == FSTATE_FOUND) {
1052 		printf("Device not complete number or * is missing\n");
1053 		return;
1054 	}
1055 
1056 	for (i = 0; cfdata[i].cf_driver; i++)
1057 		if (strlen(cfdata[i].cf_driver->cd_name) == len &&
1058 		    strncasecmp(dev, cfdata[i].cf_driver->cd_name, len) == 0)
1059 			found = 1;
1060 
1061 	if (!found) {
1062 		printf("No device of this type exists.\n");
1063 		return;
1064 	}
1065 
1066 	userconf_add_read("Clone Device (DevNo, 'q' or '?')",
1067 	    'a', dev, len, &val);
1068 
1069 	if (val != -1) {
1070 		orig = val;
1071 		new = cfdata[val];
1072 		new.cf_unit = unit;
1073 		new.cf_fstate = state;
1074 		userconf_add_read("Insert before Device (DevNo, 'q' or '?')",
1075 		    'i', dev, len, &val);
1076 	}
1077 
1078 	if (val != -1) {
1079 		/* XXX add cmd 'a' <orig> <val> eoc */
1080 		userconf_hist_cmd('a');
1081 		userconf_hist_int(orig);
1082 		userconf_hist_int(unit);
1083 		userconf_hist_int(state);
1084 		userconf_hist_int(val);
1085 		userconf_hist_eoc();
1086 
1087 		/* Insert the new record */
1088 		for (i = userconf_maxdev; val <= i; i--)
1089 			cfdata[i+1] = cfdata[i];
1090 		cfdata[val] = new;
1091 
1092 		/* Fix indexs in pv */
1093 		for (i = 0; i < pv_size; i++) {
1094 			if (pv[i] != -1 && pv[i] >= val)
1095 				pv[i]++;
1096 		}
1097 
1098 		/* Fix indexs in cfroots */
1099 		for (i = 0; i < cfroots_size; i++) {
1100 			if (cfroots[i] != -1 && cfroots[i] >= val)
1101 				cfroots[i]++;
1102 		}
1103 
1104 		userconf_maxdev++;
1105 
1106 		max_unit = -1;
1107 
1108 		/* Find max unit number of the device type */
1109 
1110 		i = 0;
1111 		while (cfdata[i].cf_attach != 0) {
1112 			if (strlen(cfdata[i].cf_driver->cd_name) == len &&
1113 			    strncasecmp(dev, cfdata[i].cf_driver->cd_name,
1114 			    len) == 0) {
1115 				switch (cfdata[i].cf_fstate) {
1116 				case FSTATE_NOTFOUND:
1117 				case FSTATE_DNOTFOUND:
1118 					if (cfdata[i].cf_unit > max_unit)
1119 						max_unit = cfdata[i].cf_unit;
1120 					break;
1121 				default:
1122 					break;
1123 				}
1124 			}
1125 			i++;
1126 		}
1127 
1128 		/*
1129 		 * For all * entries set unit number to max+1, and update
1130 		 * cf_starunit1 if necessary.
1131 		 */
1132 		max_unit++;
1133 		star_unit = -1;
1134 
1135 		i = 0;
1136 		while (cfdata[i].cf_attach != 0) {
1137 			if (strlen(cfdata[i].cf_driver->cd_name) == len &&
1138 			    strncasecmp(dev, cfdata[i].cf_driver->cd_name,
1139 			    len) == 0) {
1140 				switch (cfdata[i].cf_fstate) {
1141 				case FSTATE_NOTFOUND:
1142 				case FSTATE_DNOTFOUND:
1143 					if (cfdata[i].cf_unit > star_unit)
1144 						star_unit = cfdata[i].cf_unit;
1145 					break;
1146 				default:
1147 					break;
1148 				}
1149 			}
1150 			i++;
1151 		}
1152 		star_unit++;
1153 
1154 		i = 0;
1155 		while (cfdata[i].cf_attach != 0) {
1156 			if (strlen(cfdata[i].cf_driver->cd_name) == len &&
1157 			    strncasecmp(dev, cfdata[i].cf_driver->cd_name,
1158 			    len) == 0) {
1159 				switch (cfdata[i].cf_fstate) {
1160 				case FSTATE_STAR:
1161 				case FSTATE_DSTAR:
1162 					cfdata[i].cf_unit = max_unit;
1163 					if (cfdata[i].cf_starunit1 < star_unit)
1164 						cfdata[i].cf_starunit1 =
1165 						    star_unit;
1166 					break;
1167 				default:
1168 					break;
1169 				}
1170 			}
1171 			i++;
1172 		}
1173 		userconf_pdev(val);
1174 	}
1175 
1176 	/* cf_attach, cf_driver, cf_unit, cf_fstate, cf_loc, cf_flags,
1177 	   cf_parents, cf_locnames, and cf_locnames */
1178 }
1179 
1180 int
1181 userconf_parse(char *cmd)
1182 {
1183 	char *c, *v;
1184 	int i = 0, j = 0, k, a;
1185 	short unit, state;
1186 
1187 	c = cmd;
1188 	while (*c == ' ' || *c == '\t')
1189 		c++;
1190 	v = c;
1191 	while (*c != ' ' && *c != '\t' && *c != '\n' && *c != '\0') {
1192 		c++;
1193 		i++;
1194 	}
1195 
1196 	k = -1;
1197 	while (*userconf_cmds[j] != '\0') {
1198 		if (strlen(userconf_cmds[j]) == i) {
1199 			if (strncasecmp(v, userconf_cmds[j], i) == 0)
1200 				k = j;
1201 		}
1202 		j += 2;
1203 	}
1204 
1205 	while (*c == ' ' || *c == '\t' || *c == '\n')
1206 		c++;
1207 
1208 	if (k == -1) {
1209 		if (*v != '\n')
1210 			printf("Unknown command, try help\n");
1211 	} else {
1212 		switch (*userconf_cmds[k+1]) {
1213 		case 'L':
1214 			if (*c == '\0')
1215 				printf("Argument expected\n");
1216 			else if (userconf_number(c, &a) == 0)
1217 				userconf_lines = a;
1218 			else
1219 				printf("Unknown argument\n");
1220 			break;
1221 		case 'a':
1222 			if (*c == '\0')
1223 				printf("Dev expected\n");
1224 			else if (userconf_device(c, &a, &unit, &state) == 0)
1225 				userconf_add(c, a, unit, state);
1226 			else
1227 				printf("Unknown argument\n");
1228 			break;
1229 		case 'b':
1230 			if (*c == '\0')
1231 				printf("8|10|16 expected\n");
1232 			else if (userconf_number(c, &a) == 0) {
1233 				if (a == 8 || a == 10 || a == 16) {
1234 					userconf_base = a;
1235 				} else {
1236 					printf("8|10|16 expected\n");
1237 				}
1238 			} else
1239 				printf("Unknown argument\n");
1240 			break;
1241 		case 'c':
1242 			if (*c == '\0')
1243 				printf("DevNo or Dev expected\n");
1244 			else if (userconf_number(c, &a) == 0)
1245 				userconf_change(a);
1246 			else if (userconf_device(c, &a, &unit, &state) == 0)
1247 				userconf_common_dev(c, a, unit, state, UC_CHANGE);
1248 			else
1249 				printf("Unknown argument\n");
1250 			break;
1251 #if defined(DDB)
1252 		case 'D':
1253 			Debugger();
1254 			break;
1255 #endif
1256 		case 'd':
1257 			if (*c == '\0')
1258 				printf("Attr, DevNo or Dev expected\n");
1259 			else if (userconf_attr(c, &a) == 0)
1260 				userconf_common_attr(c, a, UC_DISABLE);
1261 			else if (userconf_number(c, &a) == 0)
1262 				userconf_disable(a);
1263 			else if (userconf_device(c, &a, &unit, &state) == 0)
1264 				userconf_common_dev(c, a, unit, state, UC_DISABLE);
1265 			else
1266 				printf("Unknown argument\n");
1267 			break;
1268 		case 'e':
1269 			if (*c == '\0')
1270 				printf("Attr, DevNo or Dev expected\n");
1271 			else if (userconf_attr(c, &a) == 0)
1272 				userconf_common_attr(c, a, UC_ENABLE);
1273 			else if (userconf_number(c, &a) == 0)
1274 				userconf_enable(a);
1275 			else if (userconf_device(c, &a, &unit, &state) == 0)
1276 				userconf_common_dev(c, a, unit, state, UC_ENABLE);
1277 			else
1278 				printf("Unknown argument\n");
1279 			break;
1280 		case 'f':
1281 			if (*c == '\0')
1282 				printf("DevNo or Dev expected\n");
1283 			else if (userconf_number(c, &a) == 0)
1284 				userconf_pdev(a);
1285 			else if (userconf_device(c, &a, &unit, &state) == 0)
1286 				userconf_common_dev(c, a, unit, state, UC_FIND);
1287 			else
1288 				printf("Unknown argument\n");
1289 			break;
1290 		case 'h':
1291 			userconf_help();
1292 			break;
1293 		case 'l':
1294 			if (*c == '\0')
1295 				userconf_list();
1296 			else
1297 				printf("Unknown argument\n");
1298 			break;
1299 		case 'q':
1300 			/* XXX add cmd 'q' eoc */
1301 			userconf_hist_cmd('q');
1302 			userconf_hist_eoc();
1303 			return(-1);
1304 			break;
1305 		case 's':
1306 			if (*c == '\0')
1307 				userconf_show();
1308 			else
1309 				userconf_show_attr(c);
1310 			break;
1311 		case 't':
1312 			if (*c == '\0' || userconf_number(c, &a) == 0) {
1313 				if (*c != '\0') {
1314 					tz.tz_minuteswest = a;
1315 					while (*c != '\n' && *c != '\t' &&
1316 					    *c != ' ' && *c != '\0')
1317 						c++;
1318 					while (*c == '\t' || *c == ' ')
1319 						c++;
1320 					if (*c != '\0' &&
1321 					    userconf_number(c, &a) == 0)
1322 						tz.tz_dsttime = a;
1323 					userconf_hist_cmd('t');
1324 					userconf_hist_int(tz.tz_minuteswest);
1325 					userconf_hist_int(tz.tz_dsttime);
1326 					userconf_hist_eoc();
1327 				}
1328 				printf("timezone = %d, dst = %d\n",
1329 				    tz.tz_minuteswest, tz.tz_dsttime);
1330 			} else
1331 				printf("Unknown argument\n");
1332 			break;
1333 		case 'v':
1334 			autoconf_verbose = !autoconf_verbose;
1335 			printf("autoconf verbose %sabled\n",
1336 			    autoconf_verbose ? "en" : "dis");
1337 			break;
1338 		default:
1339 			printf("Unknown command\n");
1340 			break;
1341 		}
1342 	}
1343 	return(0);
1344 }
1345 
1346 void
1347 user_config(void)
1348 {
1349 	userconf_init();
1350 	printf("User Kernel Config\n");
1351 
1352 	cnpollc(1);
1353 	while (1) {
1354 		printf("UKC> ");
1355 		if (getsn(userconf_cmdbuf, sizeof(userconf_cmdbuf)) > 0 &&
1356 		    userconf_parse(userconf_cmdbuf))
1357 			break;
1358 	}
1359 	cnpollc(0);
1360 
1361 	printf("Continuing...\n");
1362 }
1363