1 /* $NetBSD: subr_userconf.c,v 1.27 2018/09/16 23:18:55 mrg 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.27 2018/09/16 23:18:55 mrg 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 cnpollc(1); 107 c = cngetc(); 108 cnpollc(0); 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 cngetsn(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 cnpollc(1); 397 c = cngetc(); 398 cnpollc(0); 399 printf("\n"); 400 } 401 402 if (c == 'y' || c == 'Y') { 403 404 /* XXX add cmd 'c' <devno> */ 405 userconf_hist_cmd('c'); 406 userconf_hist_int(devno); 407 408 cd = &cfdata[devno]; 409 l = cd->cf_loc; 410 ia = cfiattr_lookup(cd->cf_pspec->cfp_iattr, 0); 411 KASSERT(ia); 412 ld = ia->ci_locdesc; 413 nld = ia->ci_loclen; 414 415 for (ln = 0; ln < nld; ln++) 416 { 417 userconf_modify(&ld[ln], l); 418 419 /* XXX add *l */ 420 userconf_hist_int(*l); 421 422 l++; 423 } 424 425 printf("[%3d] ", devno); 426 userconf_pdevnam(devno); 427 printf(" changed\n"); 428 userconf_pdev(devno); 429 430 /* XXX add eoc */ 431 userconf_hist_eoc(); 432 433 } 434 } else { 435 printf("Unknown devno (max is %d)\n", userconf_maxdev); 436 } 437 } 438 439 static void 440 userconf_disable(int devno) 441 { 442 int done = 0; 443 444 if (devno <= userconf_maxdev) { 445 switch (cfdata[devno].cf_fstate) { 446 case FSTATE_NOTFOUND: 447 cfdata[devno].cf_fstate = FSTATE_DNOTFOUND; 448 break; 449 case FSTATE_STAR: 450 cfdata[devno].cf_fstate = FSTATE_DSTAR; 451 break; 452 case FSTATE_DNOTFOUND: 453 case FSTATE_DSTAR: 454 done = 1; 455 break; 456 default: 457 printf("Error unknown state\n"); 458 break; 459 } 460 461 printf("[%3d] ", devno); 462 userconf_pdevnam(devno); 463 if (done) { 464 printf(" already"); 465 } else { 466 /* XXX add cmd 'd' <devno> eoc */ 467 userconf_hist_cmd('d'); 468 userconf_hist_int(devno); 469 userconf_hist_eoc(); 470 } 471 printf(" disabled\n"); 472 } else { 473 printf("Unknown devno (max is %d)\n", userconf_maxdev); 474 } 475 } 476 477 static void 478 userconf_enable(int devno) 479 { 480 int done = 0; 481 482 if (devno <= userconf_maxdev) { 483 switch (cfdata[devno].cf_fstate) { 484 case FSTATE_DNOTFOUND: 485 cfdata[devno].cf_fstate = FSTATE_NOTFOUND; 486 break; 487 case FSTATE_DSTAR: 488 cfdata[devno].cf_fstate = FSTATE_STAR; 489 break; 490 case FSTATE_NOTFOUND: 491 case FSTATE_STAR: 492 done = 1; 493 break; 494 default: 495 printf("Error unknown state\n"); 496 break; 497 } 498 499 printf("[%3d] ", devno); 500 userconf_pdevnam(devno); 501 if (done) { 502 printf(" already"); 503 } else { 504 /* XXX add cmd 'e' <devno> eoc */ 505 userconf_hist_cmd('d'); 506 userconf_hist_int(devno); 507 userconf_hist_eoc(); 508 } 509 printf(" enabled\n"); 510 } else { 511 printf("Unknown devno (max is %d)\n", userconf_maxdev); 512 } 513 } 514 515 static void 516 userconf_help(void) 517 { 518 int j = 0, k; 519 520 printf("command args description\n"); 521 while (*userconf_cmds[j] != '\0') { 522 printf("%s", userconf_cmds[j]); 523 k = strlen(userconf_cmds[j]); 524 while (k < 10) { 525 printf(" "); 526 k++; 527 } 528 switch (*userconf_cmds[j+1]) { 529 case 'L': 530 printf("[count] number of lines before more"); 531 break; 532 case 'b': 533 printf("8|10|16 base on large numbers"); 534 break; 535 case 'c': 536 printf("devno|dev change devices"); 537 break; 538 case 'd': 539 printf("devno|dev disable devices"); 540 break; 541 case 'e': 542 printf("devno|dev enable devices"); 543 break; 544 case 'f': 545 printf("devno|dev find devices"); 546 break; 547 case 'h': 548 printf(" this message"); 549 break; 550 case 'l': 551 printf(" list configuration"); 552 break; 553 case 'q': 554 printf(" leave userconf"); 555 break; 556 default: 557 printf(" don't know"); 558 break; 559 } 560 printf("\n"); 561 j += 2; 562 } 563 } 564 565 static void 566 userconf_list(void) 567 { 568 int i = 0; 569 570 userconf_cnt = 0; 571 572 while (cfdata[i].cf_name != NULL) { 573 if (userconf_more()) 574 break; 575 userconf_pdev(i++); 576 } 577 578 userconf_cnt = -1; 579 } 580 581 static void 582 userconf_common_dev(char *dev, int len, short unit, short state, char routine) 583 { 584 int i = 0; 585 586 switch (routine) { 587 case UC_CHANGE: 588 break; 589 default: 590 userconf_cnt = 0; 591 break; 592 } 593 594 while (cfdata[i].cf_name != NULL) { 595 if (strlen(cfdata[i].cf_name) == len) { 596 597 /* 598 * Ok, if device name is correct 599 * If state == FSTATE_FOUND, look for "dev" 600 * If state == FSTATE_STAR, look for "dev*" 601 * If state == FSTATE_NOTFOUND, look for "dev0" 602 */ 603 if (strncasecmp(dev, cfdata[i].cf_name, 604 len) == 0 && 605 (state == FSTATE_FOUND || 606 (state == FSTATE_STAR && 607 (cfdata[i].cf_fstate == FSTATE_STAR || 608 cfdata[i].cf_fstate == FSTATE_DSTAR)) || 609 (state == FSTATE_NOTFOUND && 610 cfdata[i].cf_unit == unit && 611 (cfdata[i].cf_fstate == FSTATE_NOTFOUND || 612 cfdata[i].cf_fstate == FSTATE_DNOTFOUND)))) { 613 if (userconf_more()) 614 break; 615 switch (routine) { 616 case UC_CHANGE: 617 userconf_change(i); 618 break; 619 case UC_ENABLE: 620 userconf_enable(i); 621 break; 622 case UC_DISABLE: 623 userconf_disable(i); 624 break; 625 case UC_FIND: 626 userconf_pdev(i); 627 break; 628 default: 629 printf("Unknown routine /%c/\n", 630 routine); 631 break; 632 } 633 } 634 } 635 i++; 636 } 637 638 switch (routine) { 639 case UC_CHANGE: 640 break; 641 default: 642 userconf_cnt = -1; 643 break; 644 } 645 } 646 647 #if 0 648 static void 649 userconf_add_read(char *prompt, char field, char *dev, int len, int *val) 650 { 651 int ok = 0; 652 int a; 653 char *c; 654 655 *val = -1; 656 657 while (!ok) { 658 printf("%s ? ", prompt); 659 660 getsn(userconf_argbuf, sizeof(userconf_argbuf)); 661 662 c = userconf_argbuf; 663 while (*c == ' ' || *c == '\t' || *c == '\n') c++; 664 665 if (*c != '\0') { 666 if (userconf_number(c, &a) == 0) { 667 if (a > userconf_maxdev) { 668 printf("Unknown devno (max is %d)\n", 669 userconf_maxdev); 670 } else if (strncasecmp(dev, 671 cfdata[a].cf_name, len) != 0 && 672 field == 'a') { 673 printf("Not same device type\n"); 674 } else { 675 *val = a; 676 ok = 1; 677 } 678 } else if (*c == '?') { 679 userconf_common_dev(dev, len, 0, 680 FSTATE_FOUND, UC_FIND); 681 } else if (*c == 'q' || *c == 'Q') { 682 ok = 1; 683 } else { 684 printf("Unknown argument\n"); 685 } 686 } else { 687 ok = 1; 688 } 689 } 690 } 691 #endif /* 0 */ 692 693 int 694 userconf_parse(char *cmd) 695 { 696 char *c, *v; 697 int i = 0, j = 0, k, a; 698 short unit, state; 699 700 c = cmd; 701 while (*c == ' ' || *c == '\t') 702 c++; 703 v = c; 704 while (*c != ' ' && *c != '\t' && *c != '\n' && *c != '\0') { 705 c++; 706 i++; 707 } 708 709 k = -1; 710 while (*userconf_cmds[j] != '\0') { 711 if (strlen(userconf_cmds[j]) == i) { 712 if (strncasecmp(v, userconf_cmds[j], i) == 0) 713 k = j; 714 } 715 j += 2; 716 } 717 718 while (*c == ' ' || *c == '\t' || *c == '\n') 719 c++; 720 721 if (k == -1) { 722 if (*v != '\n') 723 printf("Unknown command, try help\n"); 724 } else { 725 switch (*userconf_cmds[k+1]) { 726 case 'L': 727 if (*c == '\0') 728 printf("Argument expected\n"); 729 else if (userconf_number(c, &a) == 0) 730 userconf_lines = a; 731 else 732 printf("Unknown argument\n"); 733 break; 734 case 'b': 735 if (*c == '\0') 736 printf("8|10|16 expected\n"); 737 else if (userconf_number(c, &a) == 0) { 738 if (a == 8 || a == 10 || a == 16) { 739 userconf_base = a; 740 } else { 741 printf("8|10|16 expected\n"); 742 } 743 } else 744 printf("Unknown argument\n"); 745 break; 746 case 'c': 747 if (*c == '\0') 748 printf("DevNo or Dev expected\n"); 749 else if (userconf_number(c, &a) == 0) 750 userconf_change(a); 751 else if (userconf_device(c, &a, &unit, &state) == 0) 752 userconf_common_dev(c, a, unit, state, UC_CHANGE); 753 else 754 printf("Unknown argument\n"); 755 break; 756 case 'd': 757 if (*c == '\0') 758 printf("Attr, DevNo or Dev expected\n"); 759 else if (userconf_number(c, &a) == 0) 760 userconf_disable(a); 761 else if (userconf_device(c, &a, &unit, &state) == 0) 762 userconf_common_dev(c, a, unit, state, UC_DISABLE); 763 else 764 printf("Unknown argument\n"); 765 break; 766 case 'e': 767 if (*c == '\0') 768 printf("Attr, DevNo or Dev expected\n"); 769 else if (userconf_number(c, &a) == 0) 770 userconf_enable(a); 771 else if (userconf_device(c, &a, &unit, &state) == 0) 772 userconf_common_dev(c, a, unit, state, UC_ENABLE); 773 else 774 printf("Unknown argument\n"); 775 break; 776 case 'f': 777 if (*c == '\0') 778 printf("DevNo or Dev expected\n"); 779 else if (userconf_number(c, &a) == 0) 780 userconf_pdev(a); 781 else if (userconf_device(c, &a, &unit, &state) == 0) 782 userconf_common_dev(c, a, unit, state, UC_FIND); 783 else 784 printf("Unknown argument\n"); 785 break; 786 case 'h': 787 userconf_help(); 788 break; 789 case 'l': 790 if (*c == '\0') 791 userconf_list(); 792 else 793 printf("Unknown argument\n"); 794 break; 795 case 'q': 796 /* XXX add cmd 'q' eoc */ 797 userconf_hist_cmd('q'); 798 userconf_hist_eoc(); 799 return(-1); 800 case 's': 801 default: 802 printf("Unknown command\n"); 803 break; 804 } 805 } 806 return(0); 807 } 808 809 void 810 userconf_prompt(void) 811 { 812 const char prompt[] = "uc> "; 813 814 printf("userconf: configure system autoconfiguration:\n"); 815 816 while (1) { 817 printf(prompt); 818 if (cngetsn(userconf_cmdbuf, sizeof(userconf_cmdbuf)) > 0 && 819 userconf_parse(userconf_cmdbuf)) 820 break; 821 } 822 printf("Continuing...\n"); 823 } 824