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