Handle hostnames with upper-case letters
[webmin.git] / format / format-lib.pl
1 # format-lib.pl
2 # Common functions for partitioning and formatting disks under solaris
3
4 BEGIN { push(@INC, ".."); };
5 use WebminCore;
6 &init_config();
7 &foreign_require("mount", "mount-lib.pl");
8 &foreign_require("proc", "proc-lib.pl");
9
10 %access = &get_module_acl();
11 $| = 1;
12
13 # list_disks()
14 # Returns a list of structures, one per disk
15 sub list_disks
16 {
17 local(@rv);
18 local $temp = &transname();
19 open(TEMP, ">$temp");
20 print TEMP "disk\n";
21 close(TEMP);
22 open(FORMAT, "format -f $temp |");
23 while(1) {
24         local $rv = &wait_for(FORMAT, 'Specify', '\s+\d+\. (\S+) <(.*) cyl (\d+) alt (\d+) hd (\d+) sec (\d+)>\s*(\S*)', '\s+\d+\. (\S+) <drive type unknown>', 'space for more');
25         if ($rv <= 0) { last; }
26         elsif ($rv == 1) {
27                 local $disk = { 'device' => "/dev/dsk/$matches[1]",
28                                 'type' => $matches[2] eq 'DEFAULT' ?
29                                           undef : $matches[2],
30                                 'cyl' => $matches[3],
31                                 'alt' => $matches[4],
32                                 'hd' => $matches[5],
33                                 'sec' => $matches[6],
34                                 'volume' => $matches[7] };
35                 if ($matches[1] =~ /c(\d+)t(\d+)d(\d+)$/) {
36                         $disk->{'desc'} = &text('select_device',
37                                                 "$1", "$2", "$3");
38                         }
39                 elsif ($matches[1] =~ /c(\d+)d(\d+)$/) {
40                         $disk->{'desc'} = &text('select_idedevice',
41                                                 chr($1*2 + $2 + 65));
42                         }
43                 push(@rv, $disk);
44                 }
45         }
46 close(FORMAT);
47 unlink($temp);
48 return @rv;
49 }
50
51 # disk_info(disk)
52 # Returns an array containing a disks vendor, product and revision
53 sub disk_info
54 {
55 local(@rv);
56 &open_format();
57 &choose_disk($_[0]);
58 &fast_wait_for($fh, 'format>');
59 &wprint("inquiry\n");
60 &wait_for($fh, 'Vendor:\s+(.*)\r\nProduct:\s+(.*)\r\nRevision:\s+(.*)\r\n');
61 @rv = ($matches[1],$matches[2],$matches[3]);
62 &wait_for($fh, 'format>');
63 return @rv;
64 }
65
66 # list_partitions(device)
67 # Returns a list of structures, one per partition
68 sub list_partitions
69 {
70 local(@rv, $secs, $i);
71 local @tag = &list_tags();
72 open(VTOC, "prtvtoc $_[0]s0 |");
73 while(<VTOC>) {
74         if (/(\d+)\s+sectors\/cylinder/) {
75                 $secs = $1;
76                 }
77         if (/^\s+(\d+)\s+(\S+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)/) {
78                 local $n = $1;
79                 local $part = { 'tag' => $tag[$2],
80                                 'flag' => $3 eq "00" ? "wm" :
81                                           $3 eq "01" ? "wu" :
82                                           $3 eq "10" ? "rm" : "ru",
83                                 'start' => int($4 / $secs),
84                                 'end' => int($6 / $secs),
85                                 'device' => $_[0]."s$n" };
86                 $rv[$n] = $part;
87                 }
88         }
89 close(VTOC);
90 for($i=0; $i<8 || $i<@rv; $i++) {
91         $rv[$i] = { 'tag' => 'unassigned',
92                     'flag' => 'wm',
93                     'device' => $_[0]."s$i" } if (!$rv[$i]);
94         if ($_[0] =~ /c(\d+)t(\d+)d(\d+)$/) {
95                 $rv[$i]->{'desc'} = &text('select_part',
96                                           "$1", "$2", "$3", $i);
97                 }
98         elsif ($_[0] =~ /c(\d+)d(\d+)$/) {
99                 $rv[$i]->{'desc'} = &text('select_idepart',
100                                           chr($1*2 + $2 + 65), $i);
101                 }
102         }
103 return @rv;
104
105 #&open_format();
106 #&choose_disk($_[0]);
107 #if (!&wait_for($fh, 'unformatted', 'formatted')) { return (); }
108 #&wait_for($fh, 'format>');
109 #&wprint("partition\n");
110 #&wait_for($fh, 'partition>');
111 #&wprint("print\n");
112 #&wait_for($fh, 'Blocks\r\n');
113 #while(&wait_for($fh, 'partition>', '\s+\d+\s+(\S+)\s+(\S+)\s+(\d+)(\s+-\s+(\d+))?.*\r\n')) {
114 #       local $part = { 'tag' => $matches[1],
115 #                       'flag' => $matches[2],
116 #                       'start' => $matches[3],
117 #                       'end' => $matches[5] ? $matches[5] : $matches[3] };
118 #       if ($matches[1] =~ /c(\d+)t(\d+)d(\d+)s(\d+)$/) {
119 #               $part->{'desc'} = &text('select_part', "$1", "$2", "$3", "$4");
120 #               }
121 #       push(@rv, $part);
122 #       }
123 #&wprint("quit\n");
124 #&wait_for($fh, 'format>');
125 #return @rv[0..7];
126 }
127
128 # modify_partition(disk, partition, tag, flag, start, end)
129 # Changes an existing partition
130 sub modify_partition
131 {
132 local(@rv);
133 &open_format();
134 &choose_disk($_[0]);
135 &wait_for($fh, 'format>');
136 &wprint("partition\n");
137 local $fd = &wait_for($fh, 'partition>', 'run fdisk');
138 if ($fd == 1) {
139         # Run fdisk first
140         &wprint("fdisk\n");
141         &wprint("y\n");
142         &wait_for($fh, 'partition>');
143         }
144 &wprint("$_[1]\n");
145 &wait_for($fh, 'Enter.*:'); &wprint("$_[2]\n");
146 &wait_for($fh, 'Enter.*:'); &wprint("$_[3]\n");
147 &wait_for($fh, 'Enter.*:'); &wprint("$_[4]\n");
148 &wait_for($fh, 'Enter.*:');
149 if ($_[4] || $_[5]) { &wprint(($_[5]-$_[4]+1)."c\n"); }
150 else {
151         # deleting this partition..
152         &wprint("0\n");
153         }
154 &wait_for($fh, 'partition>');
155 &wprint("label\n");
156 if (&wait_for($fh, 'continue', 'Cannot')) {
157         &error($text{'emounted'});
158         }
159 &wprint("y\n");
160 if (&wait_for($fh, 'partition>', 'no backup labels')) {
161         &error($text{'elast'});
162         }
163 &wprint("quit\n");
164 &wait_for($fh, 'format>');
165 }
166
167 # list_tags()
168 # Returns a list of all known tags
169 sub list_tags
170 {
171 return ("unassigned", "boot", "root", "swap",
172         "usr", "backup", "stand", "var", "home", "alternates", "cache");
173
174 }
175
176 # device_status(device)
177 # Returns the mount point, type and status of some device. Uses the mount module
178 # to query the list of known and mounted filesystems
179 sub device_status
180 {
181 @mounted = &foreign_call("mount", "list_mounted") if (!@mounted);
182 @mounts = &foreign_call("mount", "list_mounts") if (!@mounts);
183 local ($mounted) = grep { $_->[1] eq $_[0] } @mounted;
184 local ($mount) = grep { $_->[1] eq $_[0] } @mounts;
185 if ($mounted) { return ($mounted->[0], $mounted->[2], 1,
186                         &indexof($mount, @mounts),
187                         &indexof($mounted, @mounted)); }
188 elsif ($mount) { return ($mount->[0], $mount->[2], 0,
189                          &indexof($mount, @mounts)); }
190 else {
191         &metamap_init();
192         if ($metastat{$_[0]}) { return ("meta", "meta", 1); }
193         if ($metadb{$_[0]}) { return ("meta", "metadb", 1); }
194         return ();
195         }
196 }
197
198
199 # fstype_name(type)
200 # Returns a human-readable filesystem name
201 sub fstype_name
202 {
203 return $text{"fstype_$_[0]"} ? $text{"fstype_$_[0]"}
204                              : $text{'fstype_unknown'};
205 }
206
207 # filesystem_type(device)
208 # Calls fstyp to get the filesystem on some device
209 sub filesystem_type
210 {
211 local($out);
212 chop($out = `fstyp $_[0] 2>&1`);
213 if ($out =~ /^\S+$/) { return $out; }
214 return undef;
215 }
216
217 # fsck_error(code)
218 # Translate an error code from fsck
219 sub fsck_error
220 {
221 return $text{"fsck_$_[0]"} ? $text{"fsck_$_[0]"} : $text{'fsck_unknown'};
222 }
223
224
225 #############################################################################
226 # Internal functions
227 #############################################################################
228 # open_format()
229 # Internal function to run the 'format' command
230 sub open_format
231 {
232 return if ($format_already_open);
233 ($fh, $fpid) = &foreign_call("proc", "pty_process_exec", "format");
234 while(1) {
235         local $rv = &wait_for($fh, 'Specify.*:', 'no disks found', 'space for more');
236         if ($rv == 0) { last; }
237         elsif ($rv == 1) { &error($text{'eformat'}); }
238         else { &wprint(" "); }
239         }
240 &wprint("0\n");
241 &wait_for($fh, 'format>');
242 $format_already_open++;
243 }
244
245 sub wprint
246 {
247 syswrite($fh, $_[0], length($_[0]));
248 }
249
250 sub opt_input
251 {
252 print $_[2] ? "<tr>" : "";
253 print "<td align=right><b>$text{$_[0]}</b></td> <td nowrap>\n";
254 print "<input type=radio name=$_[0]_def value=1 checked> $text{'default'}\n";
255 print "&nbsp; <input type=radio name=$_[0]_def value=0>\n";
256 print "<input name=$_[0] size=6> $_[1]</td>";
257 print $_[2] ? "\n" : "</tr>\n";
258 }
259
260 sub opt_check
261 {
262 if ($in{"$_[0]_def"}) { return ""; }
263 elsif ($in{$_[0]} !~ /^$_[1]$/) {
264         &error(&text('opt_error', $in{$_[0]}, $text{$_[0]}));
265         }
266 else { return " $_[2] $in{$_[0]}"; }
267 }
268
269 # metamap_init()
270 # internal function to build %metastat and %metadb arrays
271 sub metamap_init
272 {
273 if ($done_metamap_init) { return; }
274 $done_metamap_init = 1;
275 if (-x $config{metastat_path} && -x $config{metadb_path}) {
276         open(METASTAT, "$config{metastat_path} 2>&1 |");
277         while(<METASTAT>) {
278                 if (/(c\d+t\d+d\d+s\d+)/) { $metastat{"/dev/dsk/$1"}++; }
279                 }
280         close(METASTAT);
281         open(METADB, "$config{metadb_path} -i 2>&1 |");
282         while(<METADB>) {
283                 if (/(c\d+t\d+d\d+s\d+)/) { $metadb{"/dev/dsk/$1"}++; }
284                 }
285         close(METADB);
286         }
287 }
288
289 sub choose_disk
290 {
291 &wprint("disk\n");
292 while(&wait_for($fh, 'Specify.*:', 'space for more')) {
293         &wprint(" ");
294         }
295 &wprint("$_[0]\n");
296 }
297
298 # can_edit_disk(device)
299 sub can_edit_disk
300 {
301 $_[0] =~ /(c\d+t\d+d\d+)/;
302 foreach (split(/\s+/, $access{'disks'})) {
303         return 1 if ($_ eq "*" || $_ eq $1);
304         }
305 return 0;
306 }
307
308 # partition_select(name, value, mode, &found)
309 # Returns HTML for selecting a disk or partition
310 # mode 0 = disk partitions
311 #      1 = disks
312 #      2 = disks and disk partitions
313 sub partition_select
314 {
315 local $rv = "<select name=$_[0]>\n";
316 local ($found, $d, $p);
317 local @dlist = &list_disks();
318 foreach $d (@dlist) {
319         if ($_[0] > 2) {
320                 local $name = $d->{'desc'};
321                 $name .= " ($d->{'type'})" if ($d->{'type'});
322                 $rv .= sprintf "<option value=%s %s>%s\n",
323                         $d->{'device'},
324                         $_[1] eq $d->{'device'} ? "selected" : "", $name;
325                 $found++ if ($_[1] eq $d->{'device'});
326                 }
327         if ($_[0] != 1) {
328                 local @parts = &list_partitions($d->{'device'});
329                 foreach $p (@parts) {
330                         local $name = $p->{'desc'};
331                         next if (!$p->{'end'});
332                         $name .= " ($p->{'tag'})" if ($p->{'tag'});
333                         $rv .= sprintf "<option %s value=%s>%s\n",
334                                 $_[1] eq $p->{'device'} ? "selected" : "",
335                                 $p->{'device'}, $name;
336                         $found++ if ($_[1] eq $p->{'device'});
337                         }
338                 }
339         }
340 if (!$found && $_[1] && !$_[3]) {
341         $rv .= "<option selected>$_[1]\n";
342         }
343 if ($_[3]) {
344         ${$_[3]} = $found;
345         }
346 $rv .= "</select>\n";
347 return $rv;
348 }
349
350 # disk_space(device)
351 # Returns the amount of total and free space for some filesystem, or an
352 # empty array if not appropriate.
353 sub disk_space
354 {
355 local $out = `df -k $_[0] 2>&1`;
356 $out =~ /(\/dev\/\S+)\s+(\d+)\s+\S+\s+(\d+)/ || return ();
357 return ($2, $3);
358 }
359