interact.c revision 1.30 1 /* $NetBSD: interact.c,v 1.30 2006/11/26 16:16:31 jmmv 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 2006/11/26 16:16:31 jmmv 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) {
512 printf("Bad offset `%s'\n", line);
513 continue;
514 } else if (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 != 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 i, w;
654 int entry;
655 int columns, width, lines;
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