1 /* $NetBSD: subr_userconf.c,v 1.25 2011/08/01 10:33:26 drochner 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.25 2011/08/01 10:33:26 drochner Exp $"); 33 34 #include <sys/param.h> 35 #include <sys/systm.h> 36 #include <sys/device.h> 37 #include <sys/time.h> 38 #include <sys/userconf.h> 39 40 #include <dev/cons.h> 41 42 extern struct cfdata cfdata[]; 43 44 static int userconf_base = 16; /* Base for "large" numbers */ 45 static int userconf_maxdev = -1; /* # of used device slots */ 46 static int userconf_totdev = -1; /* # of device slots */ 47 #if 0 48 static int userconf_maxlocnames = -1; /* # of locnames */ 49 #endif 50 static int userconf_cnt = -1; /* Line counter for ... */ 51 static int userconf_lines = 12; /* ... # of lines per page */ 52 static int userconf_histlen = 0; 53 static int userconf_histcur = 0; 54 static char userconf_history[1024]; 55 static int userconf_histsz = sizeof(userconf_history); 56 static char userconf_argbuf[40]; /* Additional input */ 57 static char userconf_cmdbuf[40]; /* Command line */ 58 static char userconf_histbuf[40]; 59 60 static int getsn(char *, int); 61 62 #define UC_CHANGE 'c' 63 #define UC_DISABLE 'd' 64 #define UC_ENABLE 'e' 65 #define UC_FIND 'f' 66 #define UC_SHOW 's' 67 68 static const char *userconf_cmds[] = { 69 "base", "b", 70 "change", "c", 71 "disable", "d", 72 "enable", "e", 73 "exit", "q", 74 "find", "f", 75 "help", "h", 76 "list", "l", 77 "lines", "L", 78 "quit", "q", 79 "?", "h", 80 "", "", 81 }; 82 83 void 84 userconf_init(void) 85 { 86 int i; 87 struct cfdata *cf; 88 89 i = 0; 90 for (cf = cfdata; cf->cf_name; cf++) 91 i++; 92 93 userconf_maxdev = i - 1; 94 userconf_totdev = i - 1; 95 96 userconf_bootinfo(); 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 || *c == ' ' || *c == '\t' || *c == '\n')) 308 c++; 309 while (c > cmd) { 310 c--; 311 if (!((*c >= '0' && *c <= '9') || *c == '*')) { 312 c++; 313 break; 314 } 315 } 316 l = c - cmd; 317 if (*c == '*') { 318 s = FSTATE_STAR; 319 c++; 320 } else { 321 while (*c >= '0' && *c <= '9') { 322 s = FSTATE_NOTFOUND; 323 u = u*10 + *c - '0'; 324 c++; 325 } 326 } 327 while (*c == ' ' || *c == '\t' || *c == '\n') 328 c++; 329 330 if (*c == '\0') { 331 *len = l; 332 *unit = u; 333 *state = s; 334 return(0); 335 } 336 337 return(-1); 338 } 339 340 static void 341 userconf_modify(const struct cflocdesc *item, int *val) 342 { 343 int ok = 0; 344 int a; 345 char *c; 346 347 while (!ok) { 348 printf("%s [", item->cld_name); 349 if (item->cld_defaultstr && (*val == item->cld_default)) 350 printf("?"); 351 else 352 userconf_pnum(*val); 353 printf("] ? "); 354 355 getsn(userconf_argbuf, sizeof(userconf_argbuf)); 356 357 c = userconf_argbuf; 358 while (*c == ' ' || *c == '\t' || *c == '\n') c++; 359 360 if (*c != '\0') { 361 if (*c == '?') { 362 if (item->cld_defaultstr) { 363 *val = item->cld_default; 364 ok = 1; 365 } else 366 printf("No default\n"); 367 } else if (userconf_number(c, &a) == 0) { 368 *val = a; 369 ok = 1; 370 } else { 371 printf("Unknown argument\n"); 372 } 373 } else { 374 ok = 1; 375 } 376 } 377 } 378 379 static void 380 userconf_change(int devno) 381 { 382 struct cfdata *cd; 383 char c = '\0'; 384 int *l; 385 int ln; 386 const struct cfiattrdata *ia; 387 const struct cflocdesc *ld; 388 int nld; 389 390 if (devno <= userconf_maxdev) { 391 392 userconf_pdev(devno); 393 394 while (c != 'y' && c != 'Y' && c != 'n' && c != 'N') { 395 printf("change (y/n) ?"); 396 c = cngetc(); 397 printf("\n"); 398 } 399 400 if (c == 'y' || c == 'Y') { 401 402 /* XXX add cmd 'c' <devno> */ 403 userconf_hist_cmd('c'); 404 userconf_hist_int(devno); 405 406 cd = &cfdata[devno]; 407 l = cd->cf_loc; 408 ia = cfiattr_lookup(cd->cf_pspec->cfp_iattr, 0); 409 KASSERT(ia); 410 ld = ia->ci_locdesc; 411 nld = ia->ci_loclen; 412 413 for (ln = 0; ln < nld; ln++) 414 { 415 userconf_modify(&ld[ln], l); 416 417 /* XXX add *l */ 418 userconf_hist_int(*l); 419 420 l++; 421 } 422 423 printf("[%3d] ", devno); 424 userconf_pdevnam(devno); 425 printf(" changed\n"); 426 userconf_pdev(devno); 427 428 /* XXX add eoc */ 429 userconf_hist_eoc(); 430 431 } 432 } else { 433 printf("Unknown devno (max is %d)\n", userconf_maxdev); 434 } 435 } 436 437 static void 438 userconf_disable(int devno) 439 { 440 int done = 0; 441 442 if (devno <= userconf_maxdev) { 443 switch (cfdata[devno].cf_fstate) { 444 case FSTATE_NOTFOUND: 445 cfdata[devno].cf_fstate = FSTATE_DNOTFOUND; 446 break; 447 case FSTATE_STAR: 448 cfdata[devno].cf_fstate = FSTATE_DSTAR; 449 break; 450 case FSTATE_DNOTFOUND: 451 case FSTATE_DSTAR: 452 done = 1; 453 break; 454 default: 455 printf("Error unknown state\n"); 456 break; 457 } 458 459 printf("[%3d] ", devno); 460 userconf_pdevnam(devno); 461 if (done) { 462 printf(" already"); 463 } else { 464 /* XXX add cmd 'd' <devno> eoc */ 465 userconf_hist_cmd('d'); 466 userconf_hist_int(devno); 467 userconf_hist_eoc(); 468 } 469 printf(" disabled\n"); 470 } else { 471 printf("Unknown devno (max is %d)\n", userconf_maxdev); 472 } 473 } 474 475 static void 476 userconf_enable(int devno) 477 { 478 int done = 0; 479 480 if (devno <= userconf_maxdev) { 481 switch (cfdata[devno].cf_fstate) { 482 case FSTATE_DNOTFOUND: 483 cfdata[devno].cf_fstate = FSTATE_NOTFOUND; 484 break; 485 case FSTATE_DSTAR: 486 cfdata[devno].cf_fstate = FSTATE_STAR; 487 break; 488 case FSTATE_NOTFOUND: 489 case FSTATE_STAR: 490 done = 1; 491 break; 492 default: 493 printf("Error unknown state\n"); 494 break; 495 } 496 497 printf("[%3d] ", devno); 498 userconf_pdevnam(devno); 499 if (done) { 500 printf(" already"); 501 } else { 502 /* XXX add cmd 'e' <devno> eoc */ 503 userconf_hist_cmd('d'); 504 userconf_hist_int(devno); 505 userconf_hist_eoc(); 506 } 507 printf(" enabled\n"); 508 } else { 509 printf("Unknown devno (max is %d)\n", userconf_maxdev); 510 } 511 } 512 513 static void 514 userconf_help(void) 515 { 516 int j = 0, k; 517 518 printf("command args description\n"); 519 while (*userconf_cmds[j] != '\0') { 520 printf("%s", userconf_cmds[j]); 521 k = strlen(userconf_cmds[j]); 522 while (k < 10) { 523 printf(" "); 524 k++; 525 } 526 switch (*userconf_cmds[j+1]) { 527 case 'L': 528 printf("[count] number of lines before more"); 529 break; 530 case 'b': 531 printf("8|10|16 base on large numbers"); 532 break; 533 case 'c': 534 printf("devno|dev change devices"); 535 break; 536 case 'd': 537 printf("devno|dev disable devices"); 538 break; 539 case 'e': 540 printf("devno|dev enable devices"); 541 break; 542 case 'f': 543 printf("devno|dev find devices"); 544 break; 545 case 'h': 546 printf(" this message"); 547 break; 548 case 'l': 549 printf(" list configuration"); 550 break; 551 case 'q': 552 printf(" leave userconf"); 553 break; 554 default: 555 printf(" don't know"); 556 break; 557 } 558 printf("\n"); 559 j += 2; 560 } 561 } 562 563 static void 564 userconf_list(void) 565 { 566 int i = 0; 567 568 userconf_cnt = 0; 569 570 while (cfdata[i].cf_name != NULL) { 571 if (userconf_more()) 572 break; 573 userconf_pdev(i++); 574 } 575 576 userconf_cnt = -1; 577 } 578 579 static void 580 userconf_common_dev(char *dev, int len, short unit, short state, char routine) 581 { 582 int i = 0; 583 584 switch (routine) { 585 case UC_CHANGE: 586 break; 587 default: 588 userconf_cnt = 0; 589 break; 590 } 591 592 while (cfdata[i].cf_name != NULL) { 593 if (strlen(cfdata[i].cf_name) == len) { 594 595 /* 596 * Ok, if device name is correct 597 * If state == FSTATE_FOUND, look for "dev" 598 * If state == FSTATE_STAR, look for "dev*" 599 * If state == FSTATE_NOTFOUND, look for "dev0" 600 */ 601 if (strncasecmp(dev, cfdata[i].cf_name, 602 len) == 0 && 603 (state == FSTATE_FOUND || 604 (state == FSTATE_STAR && 605 (cfdata[i].cf_fstate == FSTATE_STAR || 606 cfdata[i].cf_fstate == FSTATE_DSTAR)) || 607 (state == FSTATE_NOTFOUND && 608 cfdata[i].cf_unit == unit && 609 (cfdata[i].cf_fstate == FSTATE_NOTFOUND || 610 cfdata[i].cf_fstate == FSTATE_DNOTFOUND)))) { 611 if (userconf_more()) 612 break; 613 switch (routine) { 614 case UC_CHANGE: 615 userconf_change(i); 616 break; 617 case UC_ENABLE: 618 userconf_enable(i); 619 break; 620 case UC_DISABLE: 621 userconf_disable(i); 622 break; 623 case UC_FIND: 624 userconf_pdev(i); 625 break; 626 default: 627 printf("Unknown routine /%c/\n", 628 routine); 629 break; 630 } 631 } 632 } 633 i++; 634 } 635 636 switch (routine) { 637 case UC_CHANGE: 638 break; 639 default: 640 userconf_cnt = -1; 641 break; 642 } 643 } 644 645 #if 0 646 static void 647 userconf_add_read(char *prompt, char field, char *dev, int len, int *val) 648 { 649 int ok = 0; 650 int a; 651 char *c; 652 653 *val = -1; 654 655 while (!ok) { 656 printf("%s ? ", prompt); 657 658 getsn(userconf_argbuf, sizeof(userconf_argbuf)); 659 660 c = userconf_argbuf; 661 while (*c == ' ' || *c == '\t' || *c == '\n') c++; 662 663 if (*c != '\0') { 664 if (userconf_number(c, &a) == 0) { 665 if (a > userconf_maxdev) { 666 printf("Unknown devno (max is %d)\n", 667 userconf_maxdev); 668 } else if (strncasecmp(dev, 669 cfdata[a].cf_name, len) != 0 && 670 field == 'a') { 671 printf("Not same device type\n"); 672 } else { 673 *val = a; 674 ok = 1; 675 } 676 } else if (*c == '?') { 677 userconf_common_dev(dev, len, 0, 678 FSTATE_FOUND, UC_FIND); 679 } else if (*c == 'q' || *c == 'Q') { 680 ok = 1; 681 } else { 682 printf("Unknown argument\n"); 683 } 684 } else { 685 ok = 1; 686 } 687 } 688 } 689 #endif /* 0 */ 690 691 int 692 userconf_parse(char *cmd) 693 { 694 char *c, *v; 695 int i = 0, j = 0, k, a; 696 short unit, state; 697 698 c = cmd; 699 while (*c == ' ' || *c == '\t') 700 c++; 701 v = c; 702 while (*c != ' ' && *c != '\t' && *c != '\n' && *c != '\0') { 703 c++; 704 i++; 705 } 706 707 k = -1; 708 while (*userconf_cmds[j] != '\0') { 709 if (strlen(userconf_cmds[j]) == i) { 710 if (strncasecmp(v, userconf_cmds[j], i) == 0) 711 k = j; 712 } 713 j += 2; 714 } 715 716 while (*c == ' ' || *c == '\t' || *c == '\n') 717 c++; 718 719 if (k == -1) { 720 if (*v != '\n') 721 printf("Unknown command, try help\n"); 722 } else { 723 switch (*userconf_cmds[k+1]) { 724 case 'L': 725 if (*c == '\0') 726 printf("Argument expected\n"); 727 else if (userconf_number(c, &a) == 0) 728 userconf_lines = a; 729 else 730 printf("Unknown argument\n"); 731 break; 732 case 'b': 733 if (*c == '\0') 734 printf("8|10|16 expected\n"); 735 else if (userconf_number(c, &a) == 0) { 736 if (a == 8 || a == 10 || a == 16) { 737 userconf_base = a; 738 } else { 739 printf("8|10|16 expected\n"); 740 } 741 } else 742 printf("Unknown argument\n"); 743 break; 744 case 'c': 745 if (*c == '\0') 746 printf("DevNo or Dev expected\n"); 747 else if (userconf_number(c, &a) == 0) 748 userconf_change(a); 749 else if (userconf_device(c, &a, &unit, &state) == 0) 750 userconf_common_dev(c, a, unit, state, UC_CHANGE); 751 else 752 printf("Unknown argument\n"); 753 break; 754 case 'd': 755 if (*c == '\0') 756 printf("Attr, DevNo or Dev expected\n"); 757 else if (userconf_number(c, &a) == 0) 758 userconf_disable(a); 759 else if (userconf_device(c, &a, &unit, &state) == 0) 760 userconf_common_dev(c, a, unit, state, UC_DISABLE); 761 else 762 printf("Unknown argument\n"); 763 break; 764 case 'e': 765 if (*c == '\0') 766 printf("Attr, DevNo or Dev expected\n"); 767 else if (userconf_number(c, &a) == 0) 768 userconf_enable(a); 769 else if (userconf_device(c, &a, &unit, &state) == 0) 770 userconf_common_dev(c, a, unit, state, UC_ENABLE); 771 else 772 printf("Unknown argument\n"); 773 break; 774 case 'f': 775 if (*c == '\0') 776 printf("DevNo or Dev expected\n"); 777 else if (userconf_number(c, &a) == 0) 778 userconf_pdev(a); 779 else if (userconf_device(c, &a, &unit, &state) == 0) 780 userconf_common_dev(c, a, unit, state, UC_FIND); 781 else 782 printf("Unknown argument\n"); 783 break; 784 case 'h': 785 userconf_help(); 786 break; 787 case 'l': 788 if (*c == '\0') 789 userconf_list(); 790 else 791 printf("Unknown argument\n"); 792 break; 793 case 'q': 794 /* XXX add cmd 'q' eoc */ 795 userconf_hist_cmd('q'); 796 userconf_hist_eoc(); 797 return(-1); 798 case 's': 799 default: 800 printf("Unknown command\n"); 801 break; 802 } 803 } 804 return(0); 805 } 806 807 void 808 userconf_prompt(void) 809 { 810 const char prompt[] = "uc> "; 811 812 printf("userconf: configure system autoconfiguration:\n"); 813 814 while (1) { 815 printf(prompt); 816 if (getsn(userconf_cmdbuf, sizeof(userconf_cmdbuf)) > 0 && 817 userconf_parse(userconf_cmdbuf)) 818 break; 819 } 820 printf("Continuing...\n"); 821 } 822 823 /* 824 * XXX shouldn't this be a common function? 825 */ 826 static int 827 getsn(char *cp, int size) 828 { 829 char *lp; 830 int c, len; 831 832 cnpollc(1); 833 834 lp = cp; 835 len = 0; 836 for (;;) { 837 c = cngetc(); 838 switch (c) { 839 case '\n': 840 case '\r': 841 printf("\n"); 842 *lp++ = '\0'; 843 cnpollc(0); 844 return (len); 845 case '\b': 846 case '\177': 847 case '#': 848 if (len) { 849 --len; 850 --lp; 851 printf("\b \b"); 852 } 853 continue; 854 case '@': 855 case 'u'&037: 856 len = 0; 857 lp = cp; 858 printf("\n"); 859 continue; 860 default: 861 if (len + 1 >= size || c < ' ') { 862 printf("\007"); 863 continue; 864 } 865 printf("%c", c); 866 ++len; 867 *lp++ = c; 868 } 869 } 870 } 871