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