1# format-lib.pl
2# Common functions for partitioning and formatting disks under solaris
3
4BEGIN { push(@INC, ".."); };
5use 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
15sub list_disks
16{
17local(@rv);
18local $temp = &transname();
19open(TEMP, ">$temp");
20print TEMP "disk\n";
21close(TEMP);
22open(FORMAT, "format -f $temp |");
23while(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	}
46close(FORMAT);
47unlink($temp);
48return @rv;
49}
50
51# disk_info(disk)
52# Returns an array containing a disks vendor, product and revision
53sub disk_info
54{
55local(@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>');
63return @rv;
64}
65
66# list_partitions(device)
67# Returns a list of structures, one per partition
68sub list_partitions
69{
70local(@rv, $secs, $i);
71local @tag = &list_tags();
72open(VTOC, "prtvtoc $_[0]s0 |");
73while(<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	}
89close(VTOC);
90for($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	}
103return @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
130sub modify_partition
131{
132local(@rv);
133&open_format();
134&choose_disk($_[0]);
135&wait_for($fh, 'format>');
136&wprint("partition\n");
137local $fd = &wait_for($fh, 'partition>', 'run fdisk');
138if ($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.*:');
149if ($_[4] || $_[5]) { &wprint(($_[5]-$_[4]+1)."c\n"); }
150else {
151	# deleting this partition..
152	&wprint("0\n");
153	}
154&wait_for($fh, 'partition>');
155&wprint("label\n");
156if (&wait_for($fh, 'continue', 'Cannot')) {
157	&error($text{'emounted'});
158	}
159&wprint("y\n");
160if (&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
169sub list_tags
170{
171return ("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
179sub device_status
180{
181@mounted = &foreign_call("mount", "list_mounted") if (!@mounted);
182@mounts = &foreign_call("mount", "list_mounts") if (!@mounts);
183local ($mounted) = grep { $_->[1] eq $_[0] } @mounted;
184local ($mount) = grep { $_->[1] eq $_[0] } @mounts;
185if ($mounted) { return ($mounted->[0], $mounted->[2], 1,
186			&indexof($mount, @mounts),
187			&indexof($mounted, @mounted)); }
188elsif ($mount) { return ($mount->[0], $mount->[2], 0,
189			 &indexof($mount, @mounts)); }
190else {
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
201sub fstype_name
202{
203return $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
209sub filesystem_type
210{
211local($out);
212chop($out = `fstyp $_[0] 2>&1`);
213if ($out =~ /^\S+$/) { return $out; }
214return undef;
215}
216
217# fsck_error(code)
218# Translate an error code from fsck
219sub fsck_error
220{
221return $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
230sub open_format
231{
232return if ($format_already_open);
233($fh, $fpid) = &foreign_call("proc", "pty_process_exec", "format");
234while(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
245sub wprint
246{
247syswrite($fh, $_[0], length($_[0]));
248}
249
250sub opt_input
251{
252print $_[2] ? "<tr>" : "";
253print "<td align=right><b>$text{$_[0]}</b></td> <td nowrap>\n";
254print "<input type=radio name=$_[0]_def value=1 checked> $text{'default'}\n";
255print "&nbsp; <input type=radio name=$_[0]_def value=0>\n";
256print "<input name=$_[0] size=6> $_[1]</td>";
257print $_[2] ? "\n" : "</tr>\n";
258}
259
260sub opt_check
261{
262if ($in{"$_[0]_def"}) { return ""; }
263elsif ($in{$_[0]} !~ /^$_[1]$/) {
264	&error(&text('opt_error', $in{$_[0]}, $text{$_[0]}));
265	}
266else { return " $_[2] $in{$_[0]}"; }
267}
268
269# metamap_init()
270# internal function to build %metastat and %metadb arrays
271sub metamap_init
272{
273if ($done_metamap_init) { return; }
274$done_metamap_init = 1;
275if (-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
289sub choose_disk
290{
291&wprint("disk\n");
292while(&wait_for($fh, 'Specify.*:', 'space for more')) {
293	&wprint(" ");
294	}
295&wprint("$_[0]\n");
296}
297
298# can_edit_disk(device)
299sub can_edit_disk
300{
301$_[0] =~ /(c\d+t\d+d\d+)/;
302foreach (split(/\s+/, $access{'disks'})) {
303	return 1 if ($_ eq "*" || $_ eq $1);
304	}
305return 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
313sub partition_select
314{
315local $rv = "<select name=$_[0]>\n";
316local ($found, $d, $p);
317local @dlist = &list_disks();
318foreach $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</option>\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</option>\n",
334				$_[1] eq $p->{'device'} ? "selected" : "",
335				$p->{'device'}, $name;
336			$found++ if ($_[1] eq $p->{'device'});
337			}
338		}
339	}
340if (!$found && $_[1] && !$_[3]) {
341	$rv .= "<option selected>$_[1]</option>\n";
342	}
343if ($_[3]) {
344	${$_[3]} = $found;
345	}
346$rv .= "</select>\n";
347return $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.
353sub disk_space
354{
355local $out = `df -k $_[0] 2>&1`;
356$out =~ /(\/dev\/\S+)\s+(\d+)\s+\S+\s+(\d+)/ || return ();
357return ($2, $3);
358}
359
360