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