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