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