1 /* $NetBSD: interact.c,v 1.19 2001/10/19 01:16:38 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.19 2001/10/19 01:16:38 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, ps; 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 (void)memcpy(&ps, p, sizeof(ps)); 449 450 for (;;) { 451 i = p->p_fstype; 452 if (i < 0 || i >= FSMAXTYPES) 453 i = 0; 454 snprintf(def, sizeof(def), "%s", fstypenames[i]); 455 i = getinput(":", "Filesystem type [?]", def, line); 456 if (i == -1) 457 return; 458 else if (i == 0) 459 break; 460 if (!strcmp(line, "?")) { 461 dumpnames("Supported file system types", 462 fstypenames, FSMAXTYPES); 463 continue; 464 } 465 for (i = 0; i < FSMAXTYPES; i++) 466 if (!strcasecmp(line, fstypenames[i])) { 467 p->p_fstype = i; 468 goto done_typename; 469 } 470 printf("Invalid file system typename `%s'\n", line); 471 continue; 472 done_typename: 473 break; 474 } 475 for (;;) { 476 defnum(lp, def, p->p_offset); 477 i = getinput(":", "Start offset", def, line); 478 if (i == -1) 479 return; 480 else if (i == 0) 481 break; 482 if ((i = getnum(lp, line, 0)) == -1) { 483 printf("Bad offset `%s'\n", line); 484 continue; 485 } 486 p->p_offset = i; 487 break; 488 } 489 for (;;) { 490 defnum(lp, def, p->p_size); 491 i = getinput(":", "Partition size ('$' for all remaining)", 492 def, line); 493 if (i == -1) 494 return; 495 else if (i == 0) 496 break; 497 if ((i = getnum(lp, line, lp->d_secperunit - p->p_offset)) 498 == -1) { 499 printf("Bad size `%s'\n", line); 500 continue; 501 } 502 p->p_size = i; 503 break; 504 } 505 506 if (chaining) { 507 int offs = -1; 508 struct partition *cp = lp->d_partitions; 509 for (i = 0; i < lp->d_npartitions; i++) { 510 if (cp[i].p_fstype != FS_UNUSED) { 511 if (offs != -1) 512 cp[i].p_offset = offs; 513 offs = cp[i].p_offset + cp[i].p_size; 514 } 515 } 516 } 517 if (memcmp(&ps, p, sizeof(ps))) 518 showpartition(stdout, lp, part, Cflag); 519 } 520 521 522 static void 523 cmd_label(struct disklabel *lp, char *s, int fd) 524 { 525 char line[BUFSIZ]; 526 int i; 527 528 i = getinput("?", "Label disk", "n", line); 529 if (i <= 0 || (*line != 'y' && *line != 'Y') ) 530 return; 531 532 if (checklabel(lp) != 0) { 533 printf("Label not written\n"); 534 return; 535 } 536 537 if (writelabel(fd, bootarea, lp) != 0) { 538 printf("Label not written\n"); 539 return; 540 } 541 printf("Label written\n"); 542 } 543 544 545 static int 546 runcmd(struct disklabel *lp, char *line, int fd) 547 { 548 struct cmds *cmd; 549 550 for (cmd = cmds; cmd->name != NULL; cmd++) 551 if (strncmp(line, cmd->name, strlen(cmd->name)) == 0) { 552 if (cmd->func == NULL) 553 return -1; 554 (*cmd->func)(lp, line, fd); 555 return 0; 556 } 557 558 if (line[1] == '\0' && 559 line[0] >= 'a' && line[0] < 'a' + getmaxpartitions()) { 560 cmd_part(lp, line, fd); 561 return 0; 562 } 563 564 printf("Unknown command %s\n", line); 565 return 1; 566 } 567 568 569 static int 570 getinput(const char *sep, const char *prompt, const char *def, char *line) 571 { 572 573 for (;;) { 574 printf("%s", prompt); 575 if (def) 576 printf(" [%s]", def); 577 printf("%s ", sep); 578 579 if (fgets(line, BUFSIZ, stdin) == NULL) 580 return -1; 581 if (line[0] == '\n' || line[0] == '\0') { 582 if (def) 583 return 0; 584 } 585 else { 586 char *p; 587 588 if ((p = strrchr(line, '\n')) != NULL) 589 *p = '\0'; 590 return 1; 591 } 592 } 593 } 594 595 static int 596 alphacmp(const void *a, const void *b) 597 { 598 599 return (strcasecmp(*(const char **)a, *(const char **)b)); 600 } 601 602 603 static void 604 dumpnames(const char *prompt, const char * const *olist, size_t numentries) 605 { 606 int i, j, w; 607 int columns, width, lines; 608 const char *p; 609 const char **list; 610 611 list = (const char **)malloc(sizeof(char *) * numentries); 612 width = 0; 613 printf("%s:\n", prompt); 614 for (i = 0; i < numentries; i++) { 615 list[i] = olist[i]; 616 w = strlen(list[i]); 617 if (w > width) 618 width = w; 619 } 620 #if 0 621 for (i = 0; i < numentries; i++) 622 printf("%s%s", i == 0 ? "" : ", ", list[i]); 623 puts(""); 624 #endif 625 (void)qsort(list, numentries, sizeof(char *), alphacmp); 626 width++; /* want two spaces between items */ 627 width = (width + 8) &~ 7; 628 629 #define ttywidth 72 630 columns = ttywidth / width; 631 #undef ttywidth 632 if (columns == 0) 633 columns = 1; 634 lines = (numentries + columns - 1) / columns; 635 for (i = 0; i < lines; i++) { 636 for (j = 0; j < columns; j++) { 637 p = list[j * lines + i]; 638 if (j == 0) 639 putc('\t', stdout); 640 if (p) { 641 fputs(p, stdout); 642 } 643 if (j * lines + i + lines >= numentries) { 644 putc('\n', stdout); 645 break; 646 } 647 w = strlen(p); 648 while (w < width) { 649 w = (w + 8) &~ 7; 650 putc('\t', stdout); 651 } 652 } 653 } 654 free(list); 655 } 656 657 658 static void 659 defnum(struct disklabel *lp, char *buf, int size) 660 { 661 662 (void) snprintf(buf, BUFSIZ, "%gc, %ds, %gM", 663 size / (float) lp->d_secpercyl, 664 size, size * (lp->d_secsize / (float) (1024 * 1024))); 665 } 666 667 668 static int 669 getnum(struct disklabel *lp, char *buf, int max) 670 { 671 char *ep; 672 double d; 673 int rv; 674 675 if (max && buf[0] == '$' && buf[1] == 0) 676 return max; 677 678 d = strtod(buf, &ep); 679 if (buf == ep) 680 return -1; 681 682 #define ROUND(a) ((((a) / lp->d_secpercyl) + \ 683 (((a) % lp->d_secpercyl) ? 1 : 0)) * lp->d_secpercyl) 684 685 switch (*ep) { 686 case '\0': 687 case 's': 688 rv = (int) d; 689 break; 690 691 case 'c': 692 rv = (int) (d * lp->d_secpercyl); 693 break; 694 695 case 'm': 696 case 'M': 697 rv = (int) (d * 1024 * 1024 / lp->d_secsize); 698 break; 699 700 default: 701 printf("Unit error %c\n", *ep); 702 return -1; 703 } 704 705 if (rounding) 706 return ROUND(rv); 707 else 708 return rv; 709 } 710 711 712 void 713 interact(struct disklabel *lp, int fd) 714 { 715 char line[BUFSIZ]; 716 717 for (;;) { 718 if (getinput(">", "partition", NULL, line) == -1) 719 return; 720 if (runcmd(lp, line, fd) == -1) 721 return; 722 } 723 } 724