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