1 /* $NetBSD: interact.c,v 1.15 2000/09/04 02:09:26 lukem Exp $ */ 2 3 /* 4 * Copyright (c) 1997 Christos Zoulas. All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 3. All advertising materials mentioning features or use of this software 15 * must display the following acknowledgement: 16 * This product includes software developed by Christos Zoulas. 17 * 4. The name of the author may not be used to endorse or promote products 18 * derived from this software without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 21 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 22 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 23 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 24 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 25 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 29 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 #include <sys/cdefs.h> 33 #ifndef lint 34 __RCSID("$NetBSD: interact.c,v 1.15 2000/09/04 02:09:26 lukem Exp $"); 35 #endif /* lint */ 36 37 #include <sys/param.h> 38 #define FSTYPENAMES 39 #define DKTYPENAMES 40 #include <sys/disklabel.h> 41 42 #include <err.h> 43 #include <stdio.h> 44 #include <string.h> 45 #include <stdlib.h> 46 #include <util.h> 47 48 #include "extern.h" 49 50 static void cmd_help __P((struct disklabel *, char *, int)); 51 static void cmd_chain __P((struct disklabel *, char *, int)); 52 static void cmd_print __P((struct disklabel *, char *, int)); 53 static void cmd_printall __P((struct disklabel *, char *, int)); 54 static void cmd_info __P((struct disklabel *, char *, int)); 55 static void cmd_part __P((struct disklabel *, char *, int)); 56 static void cmd_label __P((struct disklabel *, char *, int)); 57 static void cmd_round __P((struct disklabel *, char *, int)); 58 static void cmd_name __P((struct disklabel *, char *, int)); 59 static int runcmd __P((char *, struct disklabel *, int)); 60 static int getinput __P((const char *, const char *, const char *, char *)); 61 static int alphacmp __P((const void *, const void *)); 62 static void defnum __P((char *, struct disklabel *, int)); 63 static void dumpnames __P((const char *, const char * const *, size_t)); 64 static int getnum __P((char *, int, struct disklabel *)); 65 66 static int rounding = 0; /* sector rounding */ 67 static int chaining = 0; /* make partitions contiguous */ 68 69 static struct cmds { 70 const char *name; 71 void (*func) __P((struct disklabel *, char *, int)); 72 const char *help; 73 } cmds[] = { 74 { "?", cmd_help, "print this menu" }, 75 { "C", cmd_chain, "make partitions contiguous" }, 76 { "E", cmd_printall, "print disk label and current partition table"}, 77 { "I", cmd_info, "change label information" }, 78 { "N", cmd_name, "name the label" }, 79 { "P", cmd_print, "print current partition table" }, 80 { "Q", NULL, "quit" }, 81 { "R", cmd_round, "rounding (c)ylinders (s)ectors" }, 82 { "W", cmd_label, "write the current partition table" }, 83 { NULL, NULL, NULL } 84 }; 85 86 87 88 static void 89 cmd_help(lp, s, fd) 90 struct disklabel *lp; 91 char *s; 92 int fd; 93 { 94 struct cmds *cmd; 95 96 for (cmd = cmds; cmd->name != NULL; cmd++) 97 printf("%s\t%s\n", cmd->name, cmd->help); 98 printf("[a-%c]\tdefine named partition\n", 99 'a' + getmaxpartitions() - 1); 100 } 101 102 103 static void 104 cmd_chain(lp, s, fd) 105 struct disklabel *lp; 106 char *s; 107 int fd; 108 { 109 int i; 110 char line[BUFSIZ]; 111 112 i = getinput(":", "Automatically adjust partitions", 113 chaining ? "yes" : "no", line); 114 if (i <= 0) 115 return; 116 117 switch (line[0]) { 118 case 'y': 119 chaining = 1; 120 return; 121 case 'n': 122 chaining = 0; 123 return; 124 default: 125 printf("Invalid answer\n"); 126 return; 127 } 128 } 129 130 static void 131 cmd_printall(lp, s, fd) 132 struct disklabel *lp; 133 char *s; 134 int fd; 135 { 136 137 showinfo(stdout, lp); 138 showpartitions(stdout, lp); 139 } 140 141 static void 142 cmd_print(lp, s, fd) 143 struct disklabel *lp; 144 char *s; 145 int fd; 146 { 147 showpartitions(stdout, lp); 148 } 149 150 static void 151 cmd_info(lp, s, fd) 152 struct disklabel *lp; 153 char *s; 154 int fd; 155 { 156 char line[BUFSIZ]; 157 char def[BUFSIZ]; 158 int v, i; 159 u_int32_t u; 160 161 printf("# Current values:\n"); 162 showinfo(stdout, lp); 163 164 /* d_type */ 165 for (;;) { 166 i = lp->d_type; 167 if (i < 0 || i >= DKMAXTYPES) 168 i = 0; 169 snprintf(def, sizeof(def), "%s", dktypenames[i]); 170 i = getinput(":", "Disk type [?]", def, line); 171 if (i == -1) 172 return; 173 else if (i == 0) 174 break; 175 if (!strcmp(line, "?")) { 176 dumpnames("Supported disk types", dktypenames, 177 DKMAXTYPES); 178 continue; 179 } 180 for (i = 0; i < DKMAXTYPES; i++) { 181 if (!strcasecmp(dktypenames[i], line)) { 182 lp->d_type = i; 183 goto done_typename; 184 } 185 } 186 v = atoi(line); 187 if ((unsigned)v >= DKMAXTYPES) { 188 warnx("Unknown disk type: %s", line); 189 continue; 190 } 191 lp->d_type = v; 192 done_typename: 193 break; 194 } 195 196 /* d_typename */ 197 snprintf(def, sizeof(def), "%.*s", 198 (int) sizeof(lp->d_typename), lp->d_typename); 199 i = getinput(":", "Disk name", def, line); 200 if (i == -1) 201 return; 202 else if (i == 1) 203 (void) strncpy(lp->d_typename, line, sizeof(lp->d_typename)); 204 205 /* d_packname */ 206 cmd_name(lp, s, fd); 207 208 /* d_npartitions */ 209 for (;;) { 210 snprintf(def, sizeof(def), "%u", lp->d_npartitions); 211 i = getinput(":", "Number of partitions", def, line); 212 if (i == -1) 213 return; 214 else if (i == 0) 215 break; 216 if (sscanf(line, "%u", &u) != 1) { 217 printf("Invalid number of partitions `%s'\n", line); 218 continue; 219 } 220 lp->d_npartitions = u; 221 break; 222 } 223 224 /* d_secsize */ 225 for (;;) { 226 snprintf(def, sizeof(def), "%u", lp->d_secsize); 227 i = getinput(":", "Sector size (bytes)", def, line); 228 if (i == -1) 229 return; 230 else if (i == 0) 231 break; 232 if (sscanf(line, "%u", &u) != 1) { 233 printf("Invalid sector size `%s'\n", line); 234 continue; 235 } 236 lp->d_secsize = u; 237 break; 238 } 239 240 /* d_nsectors */ 241 for (;;) { 242 snprintf(def, sizeof(def), "%u", lp->d_nsectors); 243 i = getinput(":", "Number of sectors per track", def, line); 244 if (i == -1) 245 return; 246 else if (i == 0) 247 break; 248 if (sscanf(line, "%u", &u) != 1) { 249 printf("Invalid number of sectors `%s'\n", line); 250 continue; 251 } 252 lp->d_nsectors = u; 253 break; 254 } 255 256 /* d_ntracks */ 257 for (;;) { 258 snprintf(def, sizeof(def), "%u", lp->d_ntracks); 259 i = getinput(":", "Number of tracks per cylinder", def, line); 260 if (i == -1) 261 return; 262 else if (i == 0) 263 break; 264 if (sscanf(line, "%u", &u) != 1) { 265 printf("Invalid number of tracks `%s'\n", line); 266 continue; 267 } 268 lp->d_ntracks = u; 269 break; 270 } 271 272 /* d_secpercyl */ 273 for (;;) { 274 snprintf(def, sizeof(def), "%u", lp->d_secpercyl); 275 i = getinput(":", "Number of sectors/cylinder", def, line); 276 if (i == -1) 277 return; 278 else if (i == 0) 279 break; 280 if (sscanf(line, "%u", &u) != 1) { 281 printf("Invalid number of sector/cylinder `%s'\n", 282 line); 283 continue; 284 } 285 lp->d_secpercyl = u; 286 break; 287 } 288 289 /* d_ncylinders */ 290 for (;;) { 291 snprintf(def, sizeof(def), "%u", lp->d_ncylinders); 292 i = getinput(":", "Total number of cylinders", def, line); 293 if (i == -1) 294 return; 295 else if (i == 0) 296 break; 297 if (sscanf(line, "%u", &u) != 1) { 298 printf("Invalid sector size `%s'\n", line); 299 continue; 300 } 301 lp->d_ncylinders = u; 302 break; 303 } 304 305 /* d_secperunit */ 306 for (;;) { 307 snprintf(def, sizeof(def), "%u", lp->d_secperunit); 308 i = getinput(":", "Total number of sectors", def, line); 309 if (i == -1) 310 return; 311 else if (i == 0) 312 break; 313 if (sscanf(line, "%u", &u) != 1) { 314 printf("Invalid number of sectors `%s'\n", line); 315 continue; 316 } 317 lp->d_secperunit = u; 318 break; 319 } 320 321 /* d_rpm */ 322 323 /* d_interleave */ 324 for (;;) { 325 snprintf(def, sizeof(def), "%u", lp->d_interleave); 326 i = getinput(":", "Hardware sectors interleave", def, line); 327 if (i == -1) 328 return; 329 else if (i == 0) 330 break; 331 if (sscanf(line, "%u", &u) != 1) { 332 printf("Invalid sector interleave `%s'\n", line); 333 continue; 334 } 335 lp->d_interleave = u; 336 break; 337 } 338 339 /* d_trackskew */ 340 for (;;) { 341 snprintf(def, sizeof(def), "%u", lp->d_trackskew); 342 i = getinput(":", "Sector 0 skew, per track", def, line); 343 if (i == -1) 344 return; 345 else if (i == 0) 346 break; 347 if (sscanf(line, "%u", &u) != 1) { 348 printf("Invalid track sector skew `%s'\n", line); 349 continue; 350 } 351 lp->d_trackskew = u; 352 break; 353 } 354 355 /* d_cylskew */ 356 for (;;) { 357 snprintf(def, sizeof(def), "%u", lp->d_cylskew); 358 i = getinput(":", "Sector 0 skew, per cylinder", def, line); 359 if (i == -1) 360 return; 361 else if (i == 0) 362 break; 363 if (sscanf(line, "%u", &u) != 1) { 364 printf("Invalid cylinder sector `%s'\n", line); 365 continue; 366 } 367 lp->d_cylskew = u; 368 break; 369 } 370 371 /* d_headswitch */ 372 for (;;) { 373 snprintf(def, sizeof(def), "%u", lp->d_headswitch); 374 i = getinput(":", "Head switch time (usec)", def, line); 375 if (i == -1) 376 return; 377 else if (i == 0) 378 break; 379 if (sscanf(line, "%u", &u) != 1) { 380 printf("Invalid head switch time `%s'\n", line); 381 continue; 382 } 383 lp->d_headswitch = u; 384 break; 385 } 386 387 /* d_trkseek */ 388 for (;;) { 389 snprintf(def, sizeof(def), "%u", lp->d_trkseek); 390 i = getinput(":", "Track seek time (usec)", def, line); 391 if (i == -1) 392 return; 393 else if (i == 0) 394 break; 395 if (sscanf(line, "%u", &u) != 1) { 396 printf("Invalid track seek time `%s'\n", line); 397 continue; 398 } 399 lp->d_trkseek = u; 400 break; 401 } 402 } 403 404 static void 405 cmd_name(lp, s, fd) 406 struct disklabel *lp; 407 char *s; 408 int fd; 409 { 410 char line[BUFSIZ]; 411 char def[BUFSIZ]; 412 int i; 413 414 snprintf(def, sizeof(def), "%.*s", 415 (int) sizeof(lp->d_packname), lp->d_packname); 416 i = getinput(":", "Label name", def, line); 417 if (i <= 0) 418 return; 419 (void) strncpy(lp->d_packname, line, sizeof(lp->d_packname)); 420 } 421 422 static void 423 cmd_round(lp, s, fd) 424 struct disklabel *lp; 425 char *s; 426 int fd; 427 { 428 int i; 429 char line[BUFSIZ]; 430 431 i = getinput(":", "Rounding", rounding ? "cylinders" : "sectors", line); 432 if (i <= 0) 433 return; 434 435 switch (line[0]) { 436 case 'c': 437 rounding = 1; 438 return; 439 case 's': 440 rounding = 0; 441 return; 442 default: 443 printf("Rounding can be (c)ylinders or (s)ectors\n"); 444 return; 445 } 446 } 447 448 static void 449 cmd_part(lp, s, fd) 450 struct disklabel *lp; 451 char *s; 452 int fd; 453 { 454 int i; 455 char line[BUFSIZ]; 456 char def[BUFSIZ]; 457 int part = *s - 'a'; 458 struct partition *p = &lp->d_partitions[part]; 459 460 if (part >= lp->d_npartitions) 461 lp->d_npartitions = part + 1; 462 463 for (;;) { 464 i = p->p_fstype; 465 if (i < 0 || i >= FSMAXTYPES) 466 i = 0; 467 snprintf(def, sizeof(def), "%s", fstypenames[i]); 468 i = getinput(":", "Filesystem type [?]", def, line); 469 if (i == -1) 470 return; 471 else if (i == 0) 472 break; 473 if (!strcmp(line, "?")) { 474 dumpnames("Supported file system types", 475 fstypenames, FSMAXTYPES); 476 continue; 477 } 478 for (i = 0; i < FSMAXTYPES; i++) 479 if (!strcasecmp(line, fstypenames[i])) { 480 p->p_fstype = i; 481 goto done_typename; 482 } 483 printf("Invalid file system typename `%s'\n", line); 484 continue; 485 done_typename: 486 break; 487 } 488 for (;;) { 489 defnum(def, lp, p->p_offset); 490 i = getinput(":", "Start offset", def, line); 491 if (i == -1) 492 return; 493 else if (i == 0) 494 break; 495 if ((i = getnum(line, 0, lp)) == -1) { 496 printf("Bad offset `%s'\n", line); 497 continue; 498 } 499 p->p_offset = i; 500 break; 501 } 502 for (;;) { 503 defnum(def, lp, p->p_size); 504 i = getinput(":", "Partition size ('$' for all remaining)", 505 def, line); 506 if (i == -1) 507 return; 508 else if (i == 0) 509 break; 510 if ((i = getnum(line, lp->d_secperunit - p->p_offset, lp)) 511 == -1) { 512 printf("Bad size `%s'\n", line); 513 continue; 514 } 515 p->p_size = i; 516 break; 517 } 518 519 if (chaining) { 520 int offs = p[0].p_offset + p[0].p_size; 521 p = lp->d_partitions; 522 part = getrawpartition(); 523 for (i = 1; i < lp->d_npartitions; i++) { 524 if (i != part && p[i].p_fstype) { 525 p[i].p_offset = offs; 526 offs = p[i].p_offset + p[i].p_size; 527 } 528 } 529 } 530 } 531 532 533 static void 534 cmd_label(lp, s, fd) 535 struct disklabel *lp; 536 char *s; 537 int fd; 538 { 539 char line[BUFSIZ]; 540 int i; 541 542 i = getinput("?", "Label disk", "n", line); 543 if (i <= 0 || (*line != 'y' && *line != 'Y') ) 544 return; 545 546 if (checklabel(lp) != 0) { 547 printf("Label not written\n"); 548 return; 549 } 550 551 if (writelabel(fd, bootarea, lp) != 0) { 552 printf("Label not written\n"); 553 return; 554 } 555 printf("Label written\n"); 556 } 557 558 559 static int 560 runcmd(line, lp, fd) 561 char *line; 562 struct disklabel *lp; 563 int fd; 564 { 565 struct cmds *cmd; 566 567 for (cmd = cmds; cmd->name != NULL; cmd++) 568 if (strncmp(line, cmd->name, strlen(cmd->name)) == 0) { 569 if (cmd->func == NULL) 570 return -1; 571 (*cmd->func)(lp, line, fd); 572 return 0; 573 } 574 575 if (line[1] == '\0' && 576 line[0] >= 'a' && line[0] < 'a' + getmaxpartitions()) { 577 cmd_part(lp, line, fd); 578 return 0; 579 } 580 581 printf("Unknown command %s\n", line); 582 return 1; 583 } 584 585 586 static int 587 getinput(sep, prompt, def, line) 588 const char *sep; 589 const char *prompt; 590 const char *def; 591 char *line; 592 { 593 for (;;) { 594 printf("%s", prompt); 595 if (def) 596 printf(" [%s]", def); 597 printf("%s ", sep); 598 599 if (fgets(line, BUFSIZ, stdin) == NULL) 600 return -1; 601 if (line[0] == '\n' || line[0] == '\0') { 602 if (def) 603 return 0; 604 } 605 else { 606 char *p; 607 608 if ((p = strrchr(line, '\n')) != NULL) 609 *p = '\0'; 610 return 1; 611 } 612 } 613 } 614 615 static int 616 alphacmp(a, b) 617 const void *a, *b; 618 { 619 620 return (strcasecmp(*(const char **)a, *(const char **)b)); 621 } 622 623 624 static void 625 dumpnames(prompt, olist, numentries) 626 const char *prompt; 627 const char * const *olist; 628 size_t numentries; 629 { 630 int i, j, w; 631 int columns, width, lines; 632 const char *p; 633 const char **list; 634 635 list = (const char **)malloc(sizeof(char *) * numentries); 636 width = 0; 637 printf("%s:\n", prompt); 638 for (i = 0; i < numentries; i++) { 639 list[i] = olist[i]; 640 w = strlen(list[i]); 641 if (w > width) 642 width = w; 643 } 644 #if 0 645 for (i = 0; i < numentries; i++) 646 printf("%s%s", i == 0 ? "" : ", ", list[i]); 647 puts(""); 648 #endif 649 (void)qsort((void *)list, numentries, sizeof(char *), alphacmp); 650 width++; /* want two spaces between items */ 651 width = (width + 8) &~ 7; 652 653 #define ttywidth 72 654 columns = ttywidth / width; 655 #undef ttywidth 656 if (columns == 0) 657 columns = 1; 658 lines = (numentries + columns - 1) / columns; 659 for (i = 0; i < lines; i++) { 660 for (j = 0; j < columns; j++) { 661 p = list[j * lines + i]; 662 if (j == 0) 663 putc('\t', stdout); 664 if (p) { 665 fputs(p, stdout); 666 } 667 if (j * lines + i + lines >= numentries) { 668 putc('\n', stdout); 669 break; 670 } 671 w = strlen(p); 672 while (w < width) { 673 w = (w + 8) &~ 7; 674 putc('\t', stdout); 675 } 676 } 677 } 678 free(list); 679 } 680 681 682 static void 683 defnum(buf, lp, size) 684 char *buf; 685 struct disklabel *lp; 686 int size; 687 { 688 (void) snprintf(buf, BUFSIZ, "%gc, %ds, %gM", 689 size / (float) lp->d_secpercyl, 690 size, size * (lp->d_secsize / (float) (1024 * 1024))); 691 } 692 693 694 static int 695 getnum(buf, max, lp) 696 char *buf; 697 int max; 698 struct disklabel *lp; 699 { 700 char *ep; 701 double d; 702 int rv; 703 704 if (max && buf[0] == '$' && buf[1] == 0) 705 return max; 706 707 d = strtod(buf, &ep); 708 if (buf == ep) 709 return -1; 710 711 #define ROUND(a) ((a / lp->d_secpercyl) + \ 712 ((a % lp->d_secpercyl) ? 1 : 0)) * lp->d_secpercyl 713 714 switch (*ep) { 715 case '\0': 716 case 's': 717 rv = (int) d; 718 break; 719 720 case 'c': 721 rv = (int) (d * lp->d_secpercyl); 722 break; 723 724 case 'm': 725 case 'M': 726 rv = (int) (d * 1024 * 1024 / lp->d_secsize); 727 break; 728 729 default: 730 printf("Unit error %c\n", *ep); 731 return -1; 732 } 733 734 if (rounding) 735 return ROUND(rv); 736 else 737 return rv; 738 } 739 740 741 void 742 interact(lp, fd) 743 struct disklabel *lp; 744 int fd; 745 { 746 char line[BUFSIZ]; 747 748 for (;;) { 749 if (getinput(">", "partition", NULL, line) == -1) 750 return; 751 if (runcmd(line, lp, fd) == -1) 752 return; 753 } 754 } 755