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