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