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