interact.c revision 1.30.32.1 1 /* $NetBSD: interact.c,v 1.30.32.1 2011/05/20 08:11:18 matt 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.30.32.1 2011/05/20 08:11:18 matt 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 intmax_t getnum(struct disklabel *, char *, intmax_t);
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 intmax_t im;
453 char line[BUFSIZ];
454 char def[BUFSIZ];
455 int part;
456 struct partition *p, ps;
457
458 part = s[0] - 'a';
459 p = &lp->d_partitions[part];
460 if (part >= lp->d_npartitions)
461 lp->d_npartitions = part + 1;
462
463 (void)memcpy(&ps, p, sizeof(ps));
464
465 for (;;) {
466 i = p->p_fstype;
467 if (i < 0 || i >= FSMAXTYPES)
468 i = 0;
469 snprintf(def, sizeof(def), "%s", fstypenames[i]);
470 i = getinput(":", "Filesystem type [?]", def, line);
471 if (i == -1)
472 return;
473 else if (i == 0)
474 break;
475 if (!strcmp(line, "?")) {
476 dumpnames("Supported file system types",
477 fstypenames, FSMAXTYPES);
478 continue;
479 }
480 for (i = 0; i < FSMAXTYPES; i++)
481 if (!strcasecmp(line, fstypenames[i])) {
482 p->p_fstype = i;
483 goto done_typename;
484 }
485 printf("Invalid file system typename `%s'\n", line);
486 continue;
487 done_typename:
488 break;
489 }
490 for (;;) {
491 defnum(lp, def, p->p_offset);
492 i = getinput(":",
493 "Start offset ('x' to start after partition 'x')",
494 def, line);
495 if (i == -1)
496 return;
497 else if (i == 0)
498 break;
499 if (line[1] == '\0' &&
500 line[0] >= 'a' && line[0] < 'a' + getmaxpartitions()) {
501 struct partition *cp = lp->d_partitions;
502
503 if ((cp[line[0] - 'a'].p_offset +
504 cp[line[0] - 'a'].p_size) >= lp->d_secperunit) {
505 printf("Bad offset `%s'\n", line);
506 continue;
507 } else {
508 p->p_offset = cp[line[0] - 'a'].p_offset +
509 cp[line[0] - 'a'].p_size;
510 }
511 } else {
512 if ((im = getnum(lp, line, 0)) == -1 || im < 0) {
513 printf("Bad offset `%s'\n", line);
514 continue;
515 } else if (im > 0xffffffffLL ||
516 (uint32_t)im > lp->d_secperunit) {
517 printf("Offset `%s' out of range\n", line);
518 continue;
519 }
520 p->p_offset = (uint32_t)im;
521 }
522 break;
523 }
524 for (;;) {
525 defnum(lp, def, p->p_size);
526 i = getinput(":", "Partition size ('$' for all remaining)",
527 def, line);
528 if (i == -1)
529 return;
530 else if (i == 0)
531 break;
532 if ((im = getnum(lp, line, lp->d_secperunit - p->p_offset))
533 == -1) {
534 printf("Bad size `%s'\n", line);
535 continue;
536 } else if (im > 0xffffffffLL ||
537 (im + p->p_offset) > lp->d_secperunit) {
538 printf("Size `%s' out of range\n", line);
539 continue;
540 }
541 p->p_size = im;
542 break;
543 }
544
545 if (memcmp(&ps, p, sizeof(ps)))
546 showpartition(stdout, lp, part, Cflag);
547 if (chaining) {
548 int offs = -1;
549 struct partition *cp = lp->d_partitions;
550 for (i = 0; i < lp->d_npartitions; i++) {
551 if (cp[i].p_fstype != FS_UNUSED) {
552 if (offs != -1 && cp[i].p_offset != offs) {
553 cp[i].p_offset = offs;
554 showpartition(stdout, lp, i, Cflag);
555 }
556 offs = cp[i].p_offset + cp[i].p_size;
557 }
558 }
559 }
560 }
561
562
563 static void
564 cmd_label(struct disklabel *lp, char *s, int fd)
565 {
566 char line[BUFSIZ];
567 int i;
568
569 i = getinput("?", "Label disk", "n", line);
570 if (i <= 0 || (*line != 'y' && *line != 'Y') )
571 return;
572
573 if (checklabel(lp) != 0) {
574 printf("Label not written\n");
575 return;
576 }
577
578 if (writelabel(fd, lp) != 0) {
579 printf("Label not written\n");
580 return;
581 }
582 printf("Label written\n");
583 }
584
585
586 static void
587 cmd_listfstypes(struct disklabel *lp, char *s, int fd)
588 {
589
590 (void)list_fs_types();
591 }
592
593
594 static int
595 runcmd(struct disklabel *lp, char *line, int fd)
596 {
597 struct cmds *cmd;
598
599 for (cmd = cmds; cmd->name != NULL; cmd++)
600 if (strncmp(line, cmd->name, strlen(cmd->name)) == 0) {
601 if (cmd->func == NULL)
602 return -1;
603 (*cmd->func)(lp, line, fd);
604 return 0;
605 }
606
607 if (line[1] == '\0' &&
608 line[0] >= 'a' && line[0] < 'a' + getmaxpartitions()) {
609 cmd_part(lp, line, fd);
610 return 0;
611 }
612
613 printf("Unknown command %s\n", line);
614 return 1;
615 }
616
617
618 static int
619 getinput(const char *sep, const char *prompt, const char *def, char *line)
620 {
621
622 for (;;) {
623 printf("%s", prompt);
624 if (def)
625 printf(" [%s]", def);
626 printf("%s ", sep);
627
628 if (fgets(line, BUFSIZ, stdin) == NULL)
629 return -1;
630 if (line[0] == '\n' || line[0] == '\0') {
631 if (def)
632 return 0;
633 }
634 else {
635 char *p;
636
637 if ((p = strrchr(line, '\n')) != NULL)
638 *p = '\0';
639 return 1;
640 }
641 }
642 }
643
644 static int
645 alphacmp(const void *a, const void *b)
646 {
647
648 return (strcasecmp(*(const char * const*)a, *(const char * const*)b));
649 }
650
651
652 static void
653 dumpnames(const char *prompt, const char * const *olist, size_t numentries)
654 {
655 int i, w;
656 int entry;
657 int columns, width, lines;
658 const char *p;
659 const char **list;
660
661 if ((list = (const char **)malloc(sizeof(char *) * numentries)) == NULL)
662 err(1, "malloc");
663 width = 0;
664 printf("%s:\n", prompt);
665 for (i = 0; i < numentries; i++) {
666 list[i] = olist[i];
667 w = strlen(list[i]);
668 if (w > width)
669 width = w;
670 }
671 #if 0
672 for (i = 0; i < numentries; i++)
673 printf("%s%s", i == 0 ? "" : ", ", list[i]);
674 puts("");
675 #endif
676 (void)qsort(list, numentries, sizeof(char *), alphacmp);
677 width++; /* want two spaces between items */
678 width = (width + 8) &~ 7;
679
680 #define ttywidth 72
681 columns = ttywidth / width;
682 #undef ttywidth
683 if (columns == 0)
684 columns = 1;
685 lines = (numentries + columns - 1) / columns;
686 /* Output sorted by columns */
687 for (i = 0; i < lines; i++) {
688 putc('\t', stdout);
689 entry = i;
690 for (;;) {
691 p = list[entry];
692 fputs(p, stdout);
693 entry += lines;
694 if (entry >= numentries)
695 break;
696 w = strlen(p);
697 while (w < width) {
698 w = (w + 8) & ~7;
699 putc('\t', stdout);
700 }
701 }
702 putc('\n', stdout);
703 }
704 free(list);
705 }
706
707
708 static void
709 defnum(struct disklabel *lp, char *buf, uint32_t size)
710 {
711
712 (void) snprintf(buf, BUFSIZ, "%gc, %us, %gM",
713 size / (float) lp->d_secpercyl,
714 size, size * (lp->d_secsize / (float) (1024 * 1024)));
715 }
716
717
718 static intmax_t
719 getnum(struct disklabel *lp, char *buf, intmax_t defaultval)
720 {
721 char *ep;
722 double d;
723 intmax_t rv;
724
725 if (defaultval && buf[0] == '$' && buf[1] == 0)
726 return defaultval;
727
728 d = strtod(buf, &ep);
729 if (buf == ep)
730 return -1;
731
732 #define ROUND(a) ((((a) / lp->d_secpercyl) + \
733 (((a) % lp->d_secpercyl) ? 1 : 0)) * lp->d_secpercyl)
734
735 switch (*ep) {
736 case '\0':
737 case 's':
738 case 'S':
739 rv = (intmax_t) d;
740 break;
741
742 case 'c':
743 case 'C':
744 rv = (intmax_t) (d * lp->d_secpercyl);
745 break;
746
747 case 'k':
748 case 'K':
749 rv = (intmax_t) (d * 1024 / lp->d_secsize);
750 break;
751
752 case 'm':
753 case 'M':
754 rv = (intmax_t) (d * 1024 * 1024 / lp->d_secsize);
755 break;
756
757 case 'g':
758 case 'G':
759 rv = (intmax_t) (d * 1024 * 1024 * 1024 / lp->d_secsize);
760 break;
761
762 case 't':
763 case 'T':
764 rv = (intmax_t) (d * 1024 * 1024 * 1024 * 1024 / lp->d_secsize);
765 break;
766
767 default:
768 printf("Unit error %c\n", *ep);
769 printf("Valid units: (S)ectors, (C)ylinders, (K)ilo, (M)ega, "
770 "(G)iga, (T)era");
771 return -1;
772 }
773
774 if (rounding)
775 return ROUND(rv);
776 else
777 return rv;
778 }
779
780
781 void
782 interact(struct disklabel *lp, int fd)
783 {
784 char line[BUFSIZ];
785
786 for (;;) {
787 if (getinput(">", "partition", NULL, line) == -1)
788 return;
789 if (runcmd(lp, line, fd) == -1)
790 return;
791 }
792 }
793