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