]> git.proxmox.com Git - pve-installer.git/blame - proxinstall
zero start of partitions after formatting disk
[pve-installer.git] / proxinstall
CommitLineData
89a12446
DM
1#!/usr/bin/perl -w
2
7becc472
DM
3$ENV{DEBIAN_FRONTEND} = 'noninteractive';
4$ENV{LC_ALL} = 'C';
5
89a12446
DM
6use strict;
7use Getopt::Long;
62c05878 8use IPC::Open2;
89a12446
DM
9use IPC::Open3;
10use IO::File;
89a12446
DM
11use IO::Select;
12use Cwd 'abs_path';
7becc472
DM
13use Gtk3 '-init';
14use Gtk3::WebKit;
89a12446 15use Encode;
84761f93 16use String::ShellQuote;
7becc472 17use Data::Dumper;
a5af22f5 18use File::Basename;
121ebc59 19use Time::HiRes;
89a12446 20
bb77a307 21my $release = '4.4';
89a12446
DM
22
23my $kapi = `uname -r`;
24chomp $kapi;
25
6f52fc3d
DM
26my $enable_btrfs = 0;
27
89a12446
DM
28my $opt_testmode;
29
7becc472
DM
30if (!$ENV{G_SLICE} || $ENV{G_SLICE} ne "always-malloc") {
31 die "do not use slice allocator (run with 'G_SLICE=always-malloc ./proxinstall ...')\n";
32}
33
89a12446
DM
34if (!GetOptions ('testmode=s' => \$opt_testmode)) {
35 die "usage error\n";
36 exit (-1);
37}
38
6b900321
DM
39my $zfstestpool = "test_rpool";
40my $zfspoolname = $opt_testmode ? $zfstestpool : 'rpool';
5fd81672
DM
41
42my $storage_cfg_zfs = <<__EOD__;
43dir: local
44 path /var/lib/vz
45 content iso,vztmpl,backup
46
239398be 47zfspool: local-zfs
5fd81672
DM
48 pool $zfspoolname/data
49 sparse
50 content images,rootdir
51__EOD__
52
121ebc59
DM
53my $storage_cfg_btrfs = <<__EOD__;
54dir: local
55 path /var/lib/vz
56 content iso,vztmpl,backup
57 disabled
58
59btrfs: local-btrfs
60 path /var/lib/pve/local-btrfs
61 content iso,vztmpl,backup,images,rootdir
62__EOD__
63
5fd81672
DM
64my $storage_cfg_lvmthin = <<__EOD__;
65dir: local
66 path /var/lib/vz
67 content iso,vztmpl,backup
68
239398be 69lvmthin: local-lvm
5fd81672
DM
70 thinpool data
71 vgname pve
72 content rootdir,images
73__EOD__
74
75
d2120e51
DM
76sub file_read_firstline {
77 my ($filename) = @_;
78
79 my $fh = IO::File->new ($filename, "r");
80 return undef if !$fh;
81 my $res = <$fh>;
82 chomp $res if $res;
83 $fh->close;
84 return $res;
85}
86
89a12446
DM
87my $logfd = IO::File->new (">/tmp/install.log");
88
7becc472 89my $proxmox_dir = $opt_testmode ? Cwd::cwd() : "/var/lib/pve-installer";
89a12446 90
84761f93
DM
91my $grub_plattform = "pc"; # pc, efi-amd64 or efi-ia32
92
93$grub_plattform = "efi-amd64" if -d "/sys/firmware/efi";
89a12446 94
32300628 95my $IPV4OCTET = "(?:25[0-5]|(?:2[0-4]|1[0-9]|[1-9])?[0-9])";
d2120e51 96my $IPV4RE = "(?:(?:$IPV4OCTET\\.){3}$IPV4OCTET)";
b6200603
DM
97my $IPV6H16 = "(?:[0-9a-fA-F]{1,4})";
98my $IPV6LS32 = "(?:(?:$IPV4RE|$IPV6H16:$IPV6H16))";
99
100my $IPV6RE = "(?:" .
101 "(?:(?:" . "(?:$IPV6H16:){6})$IPV6LS32)|" .
102 "(?:(?:" . "::(?:$IPV6H16:){5})$IPV6LS32)|" .
103 "(?:(?:(?:" . "$IPV6H16)?::(?:$IPV6H16:){4})$IPV6LS32)|" .
104 "(?:(?:(?:(?:$IPV6H16:){0,1}$IPV6H16)?::(?:$IPV6H16:){3})$IPV6LS32)|" .
105 "(?:(?:(?:(?:$IPV6H16:){0,2}$IPV6H16)?::(?:$IPV6H16:){2})$IPV6LS32)|" .
106 "(?:(?:(?:(?:$IPV6H16:){0,3}$IPV6H16)?::(?:$IPV6H16:){1})$IPV6LS32)|" .
107 "(?:(?:(?:(?:$IPV6H16:){0,4}$IPV6H16)?::" . ")$IPV6LS32)|" .
108 "(?:(?:(?:(?:$IPV6H16:){0,5}$IPV6H16)?::" . ")$IPV6H16)|" .
109 "(?:(?:(?:(?:$IPV6H16:){0,6}$IPV6H16)?::" . ")))";
110
111my $IPRE = "(?:$IPV4RE|$IPV6RE)";
112
113
d2120e51
DM
114my $ipv4_mask_hash = {
115 '128.0.0.0' => 1,
116 '192.0.0.0' => 2,
117 '224.0.0.0' => 3,
118 '240.0.0.0' => 4,
119 '248.0.0.0' => 5,
120 '252.0.0.0' => 6,
121 '254.0.0.0' => 7,
122 '255.0.0.0' => 8,
123 '255.128.0.0' => 9,
124 '255.192.0.0' => 10,
125 '255.224.0.0' => 11,
126 '255.240.0.0' => 12,
127 '255.248.0.0' => 13,
128 '255.252.0.0' => 14,
129 '255.254.0.0' => 15,
130 '255.255.0.0' => 16,
131 '255.255.128.0' => 17,
132 '255.255.192.0' => 18,
133 '255.255.224.0' => 19,
134 '255.255.240.0' => 20,
135 '255.255.248.0' => 21,
136 '255.255.252.0' => 22,
137 '255.255.254.0' => 23,
138 '255.255.255.0' => 24,
139 '255.255.255.128' => 25,
140 '255.255.255.192' => 26,
141 '255.255.255.224' => 27,
142 '255.255.255.240' => 28,
143 '255.255.255.248' => 29,
144 '255.255.255.252' => 30
145};
146
fe44bd92
FG
147my $ipv4_reverse_mask = [
148 '0.0.0.0',
149 '128.0.0.0',
150 '192.0.0.0',
151 '224.0.0.0',
152 '240.0.0.0',
153 '248.0.0.0',
154 '252.0.0.0',
155 '254.0.0.0',
156 '255.0.0.0',
157 '255.128.0.0',
158 '255.192.0.0',
159 '255.224.0.0',
160 '255.240.0.0',
161 '255.248.0.0',
162 '255.252.0.0',
163 '255.254.0.0',
164 '255.255.0.0',
165 '255.255.128.0',
166 '255.255.192.0',
167 '255.255.224.0',
168 '255.255.240.0',
169 '255.255.248.0',
170 '255.255.252.0',
171 '255.255.254.0',
172 '255.255.255.0',
173 '255.255.255.128',
174 '255.255.255.192',
175 '255.255.255.224',
176 '255.255.255.240',
177 '255.255.255.248',
178 '255.255.255.252',
179 '255.255.255.254',
180 '255.255.255.255',
181];
182
7becc472 183my ($window, $cmdbox, $inbox, $htmlview);
c6ed3b24 184my ($next, $next_fctn, $target_hd);
89a12446 185my ($progress, $progress_status);
b6200603 186my ($ipversion, $ipaddress, $ipconf_entry_addr);
d2120e51
DM
187my ($netmask, $ipconf_entry_mask);
188my ($gateway, $ipconf_entry_gw);
189my ($dnsserver, $ipconf_entry_dns);
89a12446
DM
190my $hostname = 'proxmox';
191my $domain = 'domain.tld';
d2120e51 192my $cmdline = file_read_firstline("/proc/cmdline");
89a12446
DM
193my $ipconf;
194my $country;
195my $timezone = 'Europe/Vienna';
196my $password;
197my $mailto;
198my $keymap = 'en-us';
199my $cmap;
200
aed81ff0
DM
201# parse command line args
202
203my $config_options = {};
204
121ebc59 205if ($cmdline =~ m/\s(ext3|ext4|xfs)(\s.*)?$/) {
5c06ced5
DM
206 $config_options->{filesys} = $1;
207} else {
aeb3d07f 208 $config_options->{filesys} = 'ext4';
5c06ced5 209}
aed81ff0
DM
210
211if ($cmdline =~ m/hdsize=(\d+(\.\d+)?)[\s\n]/i) {
212 $config_options->{hdsize} = $1;
213}
214
215if ($cmdline =~ m/swapsize=(\d+(\.\d+)?)[\s\n]/i) {
216 $config_options->{swapsize} = $1;
217}
218
219if ($cmdline =~ m/maxroot=(\d+(\.\d+)?)[\s\n]/i) {
220 $config_options->{maxroot} = $1;
221}
222
223if ($cmdline =~ m/minfree=(\d+(\.\d+)?)[\s\n]/i) {
224 $config_options->{minfree} = $1;
225}
226
227if ($cmdline =~ m/maxvz=(\d+(\.\d+)?)[\s\n]/i) {
228 $config_options->{maxvz} = $1;
229}
89a12446
DM
230
231my $postfix_main_cf = <<_EOD;
232# See /usr/share/postfix/main.cf.dist for a commented, more complete version
233
234myhostname=__FQDN__
235
236smtpd_banner = \$myhostname ESMTP \$mail_name (Debian/GNU)
237biff = no
238
239# appending .domain is the MUA's job.
240append_dot_mydomain = no
241
242# Uncomment the next line to generate "delayed mail" warnings
243#delay_warning_time = 4h
244
245alias_maps = hash:/etc/aliases
246alias_database = hash:/etc/aliases
247mydestination = \$myhostname, localhost.\$mydomain, localhost
968fa90b 248relayhost =
89a12446
DM
249mynetworks = 127.0.0.0/8
250inet_interfaces = loopback-only
251recipient_delimiter = +
252
253_EOD
254
84761f93
DM
255sub shellquote {
256 my $str = shift;
257
258 return String::ShellQuote::shell_quote($str);
259}
260
261sub cmd2string {
262 my ($cmd) = @_;
263
264 die "no arguments" if !$cmd;
265
266 return $cmd if !ref($cmd);
267
268 my @qa = ();
269 foreach my $arg (@$cmd) { push @qa, shellquote($arg); }
270
271 return join (' ', @qa);
272}
273
968fa90b 274sub syscmd {
89a12446
DM
275 my ($cmd) = @_;
276
277 return run_command ($cmd, undef, undef, 1);
278}
968fa90b 279
89a12446
DM
280sub run_command {
281 my ($cmd, $func, $input, $noout) = @_;
282
84761f93
DM
283 my $cmdstr;
284 if (!ref($cmd)) {
285 $cmdstr = $cmd;
286 if ($cmd =~ m/|/) {
287 # see 'man bash' for option pipefail
288 $cmd = [ '/bin/bash', '-c', "set -o pipefail && $cmd" ];
289 } else {
290 $cmd = [ $cmd ];
291 }
292 } else {
293 $cmdstr = cmd2string($cmd);
294 }
295
89a12446 296 my $cmdtxt;
84761f93
DM
297 if ($input && ($cmdstr !~ m/chpasswd/)) {
298 $cmdtxt = "# $cmdstr <<EOD\n$input";
89a12446
DM
299 chomp $cmdtxt;
300 $cmdtxt .= "\nEOD\n";
301 } else {
84761f93 302 $cmdtxt = "# $cmdstr\n";
89a12446 303 }
4a5dbe69
DM
304
305 if ($opt_testmode) {
306 print $cmdtxt;
307 STDOUT->flush();
308 }
309
89a12446
DM
310 print $logfd $cmdtxt;
311
312 my $reader = IO::File->new();
313 my $writer = IO::File->new();
314 my $error = IO::File->new();
315
316 my $orig_pid = $$;
317
318 my $pid;
319 eval {
84761f93 320 $pid = open3 ($writer, $reader, $error, @$cmd) || die $!;
89a12446
DM
321 };
322
323 my $err = $@;
324
325 # catch exec errors
326 if ($orig_pid != $$) {
968fa90b
DM
327 POSIX::_exit (1);
328 kill ('KILL', $$);
89a12446
DM
329 }
330
331 die $err if $err;
332
333 print $writer $input if defined $input;
334 close $writer;
335
336 my $select = new IO::Select;
337 $select->add ($reader);
338 $select->add ($error);
339
340 my ($ostream, $logout) = ('', '', '');
341
342 while ($select->count) {
343 my @handles = $select->can_read (0.2);
344
d2120e51 345 Gtk3::main_iteration() while Gtk3::events_pending();
89a12446
DM
346
347 next if !scalar (@handles); # timeout
348
349 foreach my $h (@handles) {
350 my $buf = '';
351 my $count = sysread ($h, $buf, 4096);
352 if (!defined ($count)) {
353 my $err = $!;
354 kill (9, $pid);
355 waitpid ($pid, 0);
356 die "command '$cmd' failed: $err";
357 }
358 $select->remove ($h) if !$count;
359 if ($h eq $reader) {
360 $ostream .= $buf if !($noout || $func);
361 $logout .= $buf;
362 while ($logout =~ s/^([^\010\r\n]*)(\r|\n|(\010)+|\r\n)//s) {
363 my $line = $1;
364 &$func($line) if $func;
365 }
366
367 } elsif ($h eq $error) {
368 $ostream .= $buf if !($noout || $func);
369 }
370 print $buf;
371 STDOUT->flush();
372 print $logfd $buf;
373 }
374 }
375
376 &$func($logout) if $func;
377
378 my $rv = waitpid ($pid, 0);
379
380 return $? if $noout; # behave like standard system();
381
382 my $ec = ($? >> 8);
383
968fa90b 384 if ($ec) {
4a5dbe69 385 die "command '$cmdstr' failed with exit code $ec";
89a12446
DM
386 }
387
388 return $ostream;
389}
390
391sub detect_country {
392
393 print "trying to detect country...\n";
62c05878
DM
394 my $cpid = open2(\*TMP, undef, "traceroute -N 1 -q 1 -n 8.8.8.8");
395 return undef if !$cpid;
968fa90b 396
89a12446
DM
397 my $country;
398
399 my $previous_alarm = alarm (10);
400 eval {
401 local $SIG{ALRM} = sub { die "timed out!\n" };
402 my $line;
403 while (defined ($line = <TMP>)) {
404 print $logfd "DC TRACEROUTE: $line";
405 if ($line =~ m/\s*\d+\s+(\d+\.\d+\.\d+\.\d+)\s/) {
406 my $geoip = `geoiplookup $1`;
407 print $logfd "DC GEOIP: $geoip";
408 if ($geoip =~ m/GeoIP Country Edition:\s*([A-Z]+),/) {
409 $country = lc ($1);
62c05878 410 print $logfd "DC FOUND: $country\n";
89a12446
DM
411 last;
412 }
413 }
414 }
415 };
416
417 my $err = $@;
418
419 alarm ($previous_alarm);
420
421 close (TMP);
422
423 if ($err) {
424 print "unable to detect country - $err\n";
425 } elsif ($country) {
426 print "detected country: " . uc($country) . "\n";
427 } else {
428 print "unable to detect country\n";
429 }
430
431 return $country;
432}
433
434sub get_memtotal {
435
436 open (MEMINFO, "/proc/meminfo");
437
438 my $res = 512; # default to 512 if something goes wrong
439 while (my $line = <MEMINFO>) {
440 if ($line =~ m/^MemTotal:\s+(\d+)\s*kB/i) {
441 $res = int ($1 / 1024);
968fa90b 442 }
89a12446
DM
443 }
444
445 close (MEMINFO);
446
447 return $res;
448}
449
450my $total_memory = get_memtotal();
451
452sub link_points_to {
453 my ($src, $dest) = @_;
454
455 my ($dev1,$ino1) = stat ($src);
456 my ($dev2,$ino2) = stat ($dest);
457
458 return 0 if !($dev1 && $dev2 && $ino1 && $ino2);
459
460 return $ino1 == $ino2 && $dev1 == $dev2;
461}
462
463sub find_stable_path {
464 my ($stabledir, $bdev) = @_;
465
a5af22f5
DM
466 foreach my $path (<$stabledir/*>) {
467 if (link_points_to ($path, $bdev)) {
468 return wantarray ? ($path, basename($path)) : $path;
89a12446 469 }
89a12446 470 }
89a12446
DM
471}
472
473sub find_dev_by_uuid {
474 my $bdev = shift;
475
476 my ($full_path, $name) = find_stable_path ("/dev/disk/by-uuid", $bdev);
477
478 return $name;
479}
480
481sub hd_list {
482
483 my $res = ();
484
485 if ($opt_testmode) {
486 push @$res, [-1, $opt_testmode, int((-s $opt_testmode)/512), "TESTDISK"];
121ebc59 487 return $res;
89a12446
DM
488 }
489
490 my $count = 0;
491
492 foreach my $bd (</sys/block/*>) {
493 next if $bd =~ m|^/sys/block/ram\d+$|;
494 next if $bd =~ m|^/sys/block/loop\d+$|;
495 next if $bd =~ m|^/sys/block/md\d+$|;
496 next if $bd =~ m|^/sys/block/dm-.*$|;
497 next if $bd =~ m|^/sys/block/fd\d+$|;
498 next if $bd =~ m|^/sys/block/sr\d+$|;
499
d2120e51 500 my $dev = file_read_firstline("$bd/dev");
89a12446 501 chomp $dev;
968fa90b 502
89a12446
DM
503 next if !$dev;
504
505 my $info = `udevadm info --path $bd --query all`;
506 next if !$info;
507
508 next if $info !~ m/^E: DEVTYPE=disk$/m;
509
510 next if $info =~ m/^E: ID_CDROM/m;
511
512 my ($name) = $info =~ m/^N: (\S+)$/m;
513
968fa90b 514 if ($name) {
89a12446
DM
515 my $real_name = "/dev/$name";
516
d2120e51 517 my $size = file_read_firstline("$bd/size");
89a12446
DM
518 chomp $size;
519 $size = undef if !($size && $size =~ m/^\d+$/);
520
d2120e51 521 my $model = file_read_firstline("$bd/device/model") || '';
89a12446
DM
522 $model =~ s/^\s+//;
523 $model =~ s/\s+$//;
524 if (length ($model) > 30) {
525 $model = substr ($model, 0, 30);
526 }
527 push @$res, [$count++, $real_name, $size, $model] if $size;
528 } else {
529 print STDERR "ERROR: unable to map device $dev ($bd)\n";
530 }
531 }
532
533 return $res;
534}
535
536sub read_cmap {
968fa90b 537 my $countryfn = $opt_testmode ? "/usr/share/pve-manager/country.dat" :
fafc616c 538 "/cdrom/proxmox/country.dat";
89a12446
DM
539 open (TMP, "<$countryfn") || die "unable to open '$countryfn' - $!\n";
540 my $line;
541 my $country = {};
542 my $countryhash = {};
543 my $kmap = {};
544 my $kmaphash = {};
545 while (defined ($line = <TMP>)) {
546 if ($line =~ m|^map:([^\s:]+):([^:]+):([^:]+):([^:]+):([^:]+):([^:]*):$|) {
547 $kmap->{$1} = {
548 name => $2,
549 kvm => $3,
550 console => $4,
551 x11 => $5,
552 x11var => $6,
553 };
554 $kmaphash->{$2} = $1;
555 } elsif ($line =~ m|^([a-z]{2}):([^:]+):([^:]*):([^:]*):$|) {
556 $country->{$1} = {
557 name => $2,
558 kmap => $3,
559 mirror => $4,
560 };
561 $countryhash->{lc($2)} = $1;
562 } else {
563 warn "unable to parse 'country.dat' line: $line";
564 }
565 }
566 close (TMP);
567
568 my $zones = {};
569 my $cczones = {};
570 my $zonefn = "/usr/share/zoneinfo/zone.tab";
571 open (TMP, "<$zonefn") || die "unable to open '$zonefn' - $!\n";
572 while (defined ($line = <TMP>)) {
573 next if $line =~ m/^\#/;
574 next if $line =~ m/^\s*$/;
575 if ($line =~ m|^([A-Z][A-Z])\s+\S+\s+(([^/]+)/\S+)\s|) {
576 my $cc = lc($1);
577 $cczones->{$cc}->{$2} = 1;
578 $country->{$cc}->{zone} = $2 if !defined ($country->{$cc}->{zone});
579 $zones->{$2} = 1;
580
581 }
582 }
583 close (TMP);
584
585 return {
586 zones => $zones,
587 cczones => $cczones,
588 country => $country,
589 countryhash => $countryhash,
590 kmap => $kmap,
591 kmaphash => $kmaphash,
592 }
593}
594
595# search for Harddisks
596my $hds = hd_list ();
597
598sub hd_size {
599 my ($dev) = @_;
600
601 foreach my $hd (@$hds) {
602 my ($disk, $devname, $size, $model) = @$hd;
1bd457bb 603 # size is always in 512B "sectors"! convert to KB
89a12446
DM
604 return int($size/2) if $devname eq $dev;
605 }
606
eb4b1e56 607 die "no such device '$dev'\n";
89a12446
DM
608}
609
89a12446 610sub get_partition_dev {
c6ed3b24
DM
611 my ($dev, $partnum) = @_;
612
613 if ($dev =~ m|^/dev/[hxsev]d[a-z]$|) {
614 return "${dev}$partnum";
615 } elsif ($dev =~ m|^/dev/[^/]+/c\d+d\d+$|) {
616 return "${dev}p$partnum";
617 } elsif ($dev =~ m|^/dev/[^/]+/d\d+$|) {
618 return "${dev}p$partnum";
619 } elsif ($dev =~ m|^/dev/[^/]+/hd[a-z]$|) {
620 return "${dev}$partnum";
ed32cc83
WL
621 } elsif ($dev =~ m|^/dev/nvme\d+n\d+$|) {
622 return "${dev}p$partnum";
89a12446 623 } else {
c6ed3b24 624 die "unable to get device for partition $partnum on device $dev\n";
89a12446
DM
625 }
626
627}
628
8a50920c
DM
629sub file_get_contents {
630 my ($filename, $max) = @_;
631
632 my $fh = IO::File->new($filename, "r") ||
633 die "can't open '$filename' - $!\n";
634
635 local $/; # slurp mode
636
637 my $content = <$fh>;
638
639 close $fh;
640
641 return $content;
642}
643
89a12446
DM
644sub write_config {
645 my ($text, $filename) = @_;
646
647 my $fd = IO::File->new (">$filename") ||
eb4b1e56 648 die "unable to open file '$filename' - $!\n";
89a12446
DM
649 print $fd $text;
650 $fd->close();
651}
652
653sub update_progress {
654 my ($frac, $start, $end, $text) = @_;
655
656 my $part = $end - $start;
657 my $res = $start + $frac*$part;
658
659 $progress->set_fraction ($res);
660 $progress->set_text (sprintf ("%d%%", int ($res*100)));
661 $progress_status->set_text ($text) if defined ($text);
662
d2120e51 663 Gtk3::main_iteration() while Gtk3::events_pending();
89a12446
DM
664}
665
80090926
DM
666my $fssetup = {
667 ext3 => {
668 mkfs => 'mkfs.ext3 -F',
1f8d0104
DM
669 mkfs_root_opt => '',
670 mkfs_data_opt => '-m 0',
80090926
DM
671 root_mountopt => 'errors=remount-ro',
672 },
673 ext4 => {
674 mkfs => 'mkfs.ext4 -F',
1f8d0104
DM
675 mkfs_root_opt => '',
676 mkfs_data_opt => '-m 0',
80090926
DM
677 root_mountopt => 'errors=remount-ro',
678 },
679 xfs => {
680 mkfs => 'mkfs.xfs -f',
1f8d0104
DM
681 mkfs_root_opt => '',
682 mkfs_data_opt => '',
80090926
DM
683 root_mountopt => '',
684 },
685};
686
89a12446 687sub create_filesystem {
1f8d0104 688 my ($dev, $name, $type, $start, $end, $fs, $fe) = @_;
89a12446
DM
689
690 my $range = $end - $start;
691 my $rs = $start + $range*$fs;
692 my $re = $start + $range*$fe;
693 my $max = 0;
694
80090926 695 my $fsdata = $fssetup->{$type} || die "internal error - unknown file system '$type'";
1f8d0104 696 my $opts = $name eq 'root' ? $fsdata->{mkfs_root_opt} : $fsdata->{mkfs_data_opt};
80090926 697
89a12446
DM
698 update_progress (0, $rs, $re, "creating $name filesystem");
699
80090926 700 run_command ("$fsdata->{mkfs} $opts $dev", sub {
89a12446
DM
701 my $line = shift;
702
703 if ($line =~ m/Writing inode tables:\s+(\d+)\/(\d+)/) {
704 $max = $2;
705 } elsif ($max && $line =~ m/(\d+)\/$max/) {
706 update_progress (($1/$max)*0.9, $rs, $re);
707 } elsif ($line =~ m/Creating journal.*done/) {
708 update_progress (0.95, $rs, $re);
709 } elsif ($line =~ m/Writing superblocks and filesystem.*done/) {
710 update_progress (1, $rs, $re);
968fa90b 711 }
89a12446
DM
712 });
713}
714
715sub debconfig_set {
716 my ($targetdir, $dcdata) = @_;
717
718 my $cfgfile = "/tmp/debconf.txt";
719 write_config ($dcdata, "$targetdir/$cfgfile");
968fa90b
DM
720 syscmd ("chroot $targetdir debconf-set-selections $cfgfile");
721 unlink "$targetdir/$cfgfile";
89a12446
DM
722}
723
724sub diversion_add {
725 my ($targetdir, $cmd, $new_cmd) = @_;
726
727 syscmd ("chroot $targetdir dpkg-divert --package proxmox " .
728 "--add --rename $cmd") == 0 ||
729 die "unable to exec dpkg-divert\n";
730
731 syscmd ("ln -sf ${new_cmd} $targetdir/$cmd") == 0 ||
968fa90b 732 die "unable to link diversion to ${new_cmd}\n";
89a12446
DM
733}
734
735sub diversion_remove {
736 my ($targetdir, $cmd) = @_;
737
738 syscmd ("mv $targetdir/${cmd}.distrib $targetdir/${cmd};") == 0 ||
739 die "unable to remove $cmd diversion\n";
968fa90b 740
89a12446
DM
741 syscmd ("chroot $targetdir dpkg-divert --remove $cmd") == 0 ||
742 die "unable to remove $cmd diversion\n";
743}
744
121ebc59
DM
745sub btrfs_create {
746 my ($partitions, $mode) = @_;
747
748 die "unknown btrfs mode '$mode'"
749 if !($mode eq 'single' || $mode eq 'raid0' ||
750 $mode eq 'raid1' || $mode eq 'raid10');
751
752 my $cmd = ['mkfs.btrfs', '-f'];
753
754 push @$cmd, '-d', $mode, '-m', $mode;
755
756 push @$cmd, @$partitions;
757
758 syscmd($cmd);
759}
760
5c06ced5 761sub zfs_create_rpool {
5fd81672 762 my ($vdev) = @_;
486c490d 763
c7779156
FG
764 my $cmd = "zpool create -f -o cachefile=none";
765
766 $cmd .= " -o ashift=$config_options->{ashift}"
767 if defined($config_options->{ashift});
768
769 syscmd ("$cmd $zfspoolname $vdev") == 0 ||
5c06ced5
DM
770 die "unable to create zfs root pool\n";
771
772 syscmd ("zfs create $zfspoolname/ROOT") == 0 ||
7bc4f6bd 773 die "unable to create zfs $zfspoolname/ROOT volume\n";
5c06ced5 774
5fd81672
DM
775 syscmd ("zfs create $zfspoolname/data") == 0 ||
776 die "unable to create zfs $zfspoolname/data volume\n";
777
5c06ced5 778 syscmd ("zfs create $zfspoolname/ROOT/pve-1") == 0 ||
7bc4f6bd 779 die "unable to create zfs $zfspoolname/ROOT/pve-1 volume\n";
5c06ced5
DM
780
781 # disable atime during insatll
782 syscmd ("zfs set atime=off $zfspoolname") == 0 ||
783 die "unable to set zfs properties\n";
c7779156
FG
784
785 my $value = $config_options->{compress};
786 syscmd ("zfs set compression=$value $zfspoolname")
787 if defined($value) && $value ne 'off';
788
789 $value = $config_options->{checksum};
790 syscmd ("zfs set checksum=$value $zfspoolname")
791 if defined($value) && $value ne 'on';
792
793 $value = $config_options->{copies};
794 syscmd ("zfs set copies=$value $zfspoolname")
795 if defined($value) && $value != 1;
5c06ced5
DM
796}
797
7bc4f6bd 798sub zfs_create_swap {
5fd81672 799 my ($swapsize) = @_;
7bc4f6bd 800
991ec37f 801 my $cmd = "zfs create -V ${swapsize}K -b 4K";
7bc4f6bd 802
991ec37f
FG
803 $cmd .= " -o com.sun:auto-snapshot=false";
804
805 # copies for swap does not make sense
806 $cmd .= " -o copies=1";
7bc4f6bd
DM
807
808 # reduces memory pressure
991ec37f 809 $cmd .= " -o sync=always";
7bc4f6bd 810
991ec37f
FG
811 # cheapest compression to drop zero pages
812 $cmd .= " -o compression=zle";
813
814 # skip log devices
815 $cmd .= " -o logbias=throughput";
816 # only cache metadata in RAM (caching swap content does not make sense)
817 $cmd .= " -o primarycache=metadata";
818 # don't cache anything in L2ARC
819 $cmd .= " -o secondarycache=none";
820
821 $cmd .= " $zfspoolname/swap";
822 syscmd ($cmd) == 0 ||
823 die "unable to create zfs swap device\n";
c7779156 824
7bc4f6bd
DM
825 return "/dev/zvol/$zfspoolname/swap";
826}
827
dc4ad419
FG
828my $udevadm_trigger_block = sub {
829 my ($nowait) = @_;
830
831 sleep(1) if !$nowait; # give kernel time to reread part table
832
833 # trigger udev to create /dev/disk/by-uuid
834 syscmd ("udevadm trigger --subsystem-match block");
835 syscmd ("udevadm settle --timeout 10");
836};
837
c6ed3b24 838sub partition_bootable_disk {
121ebc59 839 my ($target_dev, $maxhdsize, $ptype) = @_;
89a12446 840
c6ed3b24 841 die "too dangerous" if $opt_testmode;
89a12446 842
121ebc59
DM
843 die "unknown partition type '$ptype'"
844 if !($ptype eq '8E00' || $ptype eq '8300');
845
6ab785ef 846 syscmd("sgdisk -Z ${target_dev}");
1bd457bb 847 my $hdsize = hd_size($target_dev); # size in KB (1024 bytes)
c6ed3b24 848
9b4dc6e8 849 my $restricted_hdsize_mb = 0; # 0 ==> end of partition
c6ed3b24
DM
850 if ($maxhdsize && ($maxhdsize < $hdsize)) {
851 $hdsize = $maxhdsize;
0cf4b512 852 $restricted_hdsize_mb = int($hdsize/1024) . 'M';
c6ed3b24
DM
853 }
854
855 my $hdgb = int($hdsize/(1024*1024));
eb4b1e56 856 die "hardisk '$target_dev' too small (${hdsize}GB)\n" if $hdgb < 8;
c6ed3b24 857
43b5216c 858 # 1 - BIOS boot partition (Grub Stage2): first free 1M
b15f2bdf 859 # 2 - EFI ESP: next free 256M
43b5216c 860 # 3 - OS/Data partition: rest, up to $maxhdsize in MB
c6ed3b24
DM
861
862 my $grubbootdev = get_partition_dev($target_dev, 1);
863 my $efibootdev = get_partition_dev($target_dev, 2);
a2876e48 864 my $osdev = get_partition_dev ($target_dev, 3);
aed81ff0 865
43b5216c 866 my $pcmd = ['sgdisk'];
89a12446 867
f810f5d0 868 my $pnum = 1;
d8505895 869 push @$pcmd, "-n${pnum}:1M:+1M", "-t$pnum:EF02";
b282cfe8 870
35be9ba7 871 $pnum = 2;
d8505895 872 push @$pcmd, "-n${pnum}:2M:+256M", "-t$pnum:EF00";
35be9ba7 873
f810f5d0 874 $pnum = 3;
d8505895 875 push @$pcmd, "-n${pnum}:258M:${restricted_hdsize_mb}", "-t$pnum:$ptype";
35be9ba7 876
f810f5d0 877 push @$pcmd, $target_dev;
b282cfe8 878
d8505895 879 my $os_size = $hdsize - 258*1024; # 256M + 1M + 1M alignment
89a12446 880
f810f5d0
DM
881 syscmd($pcmd) == 0 ||
882 die "unable to partition harddisk '${target_dev}'\n";
89a12446 883
dc4ad419
FG
884 &$udevadm_trigger_block();
885
886 foreach my $part ($efibootdev, $osdev) {
887 syscmd("dd if=/dev/zero of=$part bs=1M count=256") if -b $part;
888 }
889
f810f5d0
DM
890 return ($os_size, $osdev, $efibootdev);
891}
5c06ced5 892
f810f5d0
DM
893# ZFS has this use_whole_disk concept, so we try to partition the same
894# way as zfs does by default. There is room at start of disk to insert
895# a grub boot partition. But adding a EFI ESP is not possible.
896#
897# Note: zfs people think this is just a waste of space an not
898# required. Instead, you should put the ESP on another disk (log,
899# ..).
89a12446 900
f810f5d0
DM
901sub partition_bootable_zfs_disk {
902 my ($target_dev) = @_;
903
904 die "too dangerous" if $opt_testmode;
905
649d65b6 906 syscmd("sgdisk -Z ${target_dev}");
f810f5d0
DM
907 my $hdsize = hd_size($target_dev); # size in blocks (1024 bytes)
908
909 my $hdgb = int($hdsize/(1024*1024));
eb4b1e56 910 die "hardisk '$target_dev' too small (${hdsize}GB)\n" if $hdgb < 8;
f810f5d0
DM
911
912 # 1 - GRUB boot partition: 1M
913 # 2 - OS/Data partition
914 # 9 - ZFS reserved partition
915
916 my $grubbootdev = get_partition_dev($target_dev, 1);
917 my $osdev = get_partition_dev ($target_dev, 2);
918
919 my $pcmd = ['sgdisk', '-a1'];
920
921 my $pnum = 1;
922 push @$pcmd, "-n$pnum:34:2047", "-t$pnum:EF02";
923
924 $pnum = 9;
925 push @$pcmd, "-n$pnum:-8M:0", "-t$pnum:BF07";
926
927 $pnum = 2;
928 push @$pcmd, "-n$pnum:2048:0", "-t$pnum:BF01", '-c', "$pnum:zfs";
929
930 push @$pcmd, $target_dev;
c6ed3b24 931
f810f5d0 932 my $os_size = $hdsize - 1024 - 1024*8;
968fa90b 933
c6ed3b24
DM
934 syscmd($pcmd) == 0 ||
935 die "unable to partition harddisk '${target_dev}'\n";
968fa90b 936
dc4ad419
FG
937 &$udevadm_trigger_block();
938
939 syscmd("dd if=/dev/zero of=$osdev bs=1M count=16") if -b $osdev;
940
f810f5d0 941 return ($os_size, $osdev);
c6ed3b24 942}
84761f93 943
c6ed3b24
DM
944sub create_lvm_volumes {
945 my ($lvmdev, $os_size, $swap_size) = @_;
7bc4f6bd 946
c6ed3b24
DM
947 my $rootdev = '/dev/pve/root';
948 my $datadev = '/dev/pve/data';
949 my $swapfile = '/dev/pve/swap';
84761f93 950
c6ed3b24
DM
951 # we use --metadatasize 250k, which reseults in "pe_start = 512"
952 # so pe_start is aligned on a 128k boundary (advantage for SSDs)
953 syscmd ("/sbin/pvcreate --metadatasize 250k -y -ff $lvmdev") == 0 ||
eb4b1e56 954 die "unable to initialize physical volume $lvmdev\n";
c6ed3b24 955 syscmd ("/sbin/vgcreate pve $lvmdev") == 0 ||
eb4b1e56 956 die "unable to create volume group 'pve'\n";
89a12446 957
c6ed3b24
DM
958 my $hdgb = int($os_size/(1024*1024));
959 my $space = (($hdgb > 128) ? 16 : ($hdgb/8))*1024*1024;
89a12446 960
c6ed3b24
DM
961 my $maxroot;
962 if ($config_options->{maxroot}) {
963 $maxroot = $config_options->{maxroot};
964 } else {
965 $maxroot = 96;
966 }
89a12446 967
c6ed3b24 968 my $rootsize = (($hdgb > ($maxroot*4)) ? $maxroot : $hdgb/4)*1024*1024;
89a12446 969
c6ed3b24 970 my $rest = $os_size - $swap_size - $rootsize; # in KB
7bc4f6bd 971
c6ed3b24
DM
972 my $minfree;
973 if ($config_options->{minfree}) {
974 $minfree = (($config_options->{minfree}*1024*1024) >= $rest ) ? $space :
975 $config_options->{minfree}*1024*1024 ;
976 } else {
977 $minfree = $space;
978 }
7bc4f6bd 979
c6ed3b24 980 $rest = $rest - $minfree;
7bc4f6bd 981
c6ed3b24
DM
982 if ($config_options->{maxvz}) {
983 $rest = (($config_options->{maxvz}*1024*1024) <= $rest) ?
984 $config_options->{maxvz}*1024*1024 : $rest;
985 }
7bc4f6bd 986
c6ed3b24 987 syscmd ("/sbin/lvcreate -L${swap_size}K -nswap pve") == 0 ||
eb4b1e56 988 die "unable to create swap volume\n";
89a12446 989
c6ed3b24 990 syscmd ("/sbin/lvcreate -L${rootsize}K -nroot pve") == 0 ||
eb4b1e56 991 die "unable to create root volume\n";
89a12446 992
c6ed3b24 993 syscmd ("/sbin/lvcreate -L${rest}K -ndata pve") == 0 ||
eb4b1e56 994 die "unable to create data volume\n";
89a12446 995
5fd81672 996 syscmd ("/sbin/lvconvert --yes --type thin-pool pve/data") == 0 ||
eb4b1e56 997 die "unable to create data thin-pool\n";
5fd81672 998
c6ed3b24 999 syscmd ("/sbin/vgchange -a y pve") == 0 ||
eb4b1e56 1000 die "unable to activate volume group\n";
7bc4f6bd 1001
c6ed3b24
DM
1002 return ($rootdev, $datadev, $swapfile);
1003}
7bc4f6bd 1004
c6ed3b24
DM
1005sub compute_swapsize {
1006 my ($hdsize) = @_;
89a12446 1007
c6ed3b24 1008 my $hdgb = int($hdsize/(1024*1024));
5c06ced5 1009
c6ed3b24
DM
1010 my $swapsize;
1011 if ($config_options->{swapsize}) {
1012 $swapsize = $config_options->{swapsize}*1024*1024;
1013 } else {
1014 my $ss = int ($total_memory / 1024);
1015 $ss = 4 if $ss < 4;
1016 $ss = ($hdgb/8) if $ss > ($hdgb/8);
cbdfeb36 1017 $ss = 8 if $ss > 8;
c6ed3b24
DM
1018 $swapsize = $ss*1024*1024;
1019 }
d0d8ce3f
DM
1020
1021 return $swapsize;
c6ed3b24 1022}
5c06ced5 1023
121ebc59 1024
c6ed3b24 1025sub extract_data {
fafc616c 1026 my ($basefile, $targetdir) = @_;
89a12446 1027
c6ed3b24 1028 die "target '$targetdir' does not exist\n" if ! -d $targetdir;
89a12446 1029
121ebc59
DM
1030 my $starttime = [Time::HiRes::gettimeofday];
1031
c6ed3b24 1032 my $bootdevinfo = [];
84761f93 1033
c6ed3b24
DM
1034 my $datadev;
1035 my $swapfile;
1036 my $rootdev;
84761f93 1037
121ebc59
DM
1038 my $use_zfs = 0;
1039 my $use_btrfs = 0;
89092156 1040
c6ed3b24 1041 my $filesys = $config_options->{filesys};
89092156 1042
c6ed3b24
DM
1043 if ($filesys =~ m/zfs/) {
1044 $target_hd = undef; # do not use this config
1045 $use_zfs = 1;
1046 $targetdir = "/$zfspoolname/ROOT/pve-1";
121ebc59
DM
1047 } elsif ($filesys =~ m/btrfs/) {
1048 $target_hd = undef; # do not use this config
1049 $use_btrfs = 1;
c6ed3b24
DM
1050 }
1051
1052 if ($use_zfs) {
1053 my $i;
1054 for ($i = 5; $i > 0; $i--) {
1055 syscmd("modprobe zfs");
1056 last if -c "/dev/zfs";
1057 sleep(1);
1058 }
89092156 1059
c6ed3b24
DM
1060 die "unable to load zfs kernel module\n" if !$i;
1061 }
89092156 1062
c6ed3b24 1063 eval {
89a12446 1064
89a12446 1065
c6ed3b24 1066 my $maxper = 0.25;
89a12446 1067
c6ed3b24
DM
1068 update_progress (0, 0, $maxper, "create partitions");
1069
1070 if ($opt_testmode) {
89a12446 1071
6b900321
DM
1072 $rootdev = abs_path($opt_testmode);
1073 syscmd("umount $rootdev");
121ebc59 1074
6b900321 1075 if ($use_btrfs) {
121ebc59
DM
1076
1077 die "unsupported btrfs mode (for testing environment)\n"
1078 if $filesys ne 'btrfs (RAID0)';
1079
1080 btrfs_create([$rootdev], 'single');
5c06ced5 1081
121ebc59 1082 } elsif ($use_zfs) {
5c06ced5 1083
121ebc59 1084 die "unsupported zfs mode (for testing environment)\n"
c6ed3b24
DM
1085 if $filesys ne 'zfs (RAID0)';
1086
6b900321 1087 syscmd ("zpool destroy $zfstestpool");
5c06ced5 1088
5fd81672 1089 zfs_create_rpool($rootdev);
121ebc59
DM
1090
1091 } else {
1092
6b900321 1093 # nothing to do
121ebc59
DM
1094 }
1095
1096 } elsif ($use_btrfs) {
1097
1098 my ($devlist, $btrfs_mode) = get_btrfs_raid_setup();
1099 my $btrfs_partitions = [];
1100 my $disksize;
1101 foreach my $hd (@$devlist) {
1102 my $devname = @$hd[1];
1103 my ($size, $osdev, $efidev) =
1104 partition_bootable_disk($devname, undef, '8300');
1105 $rootdev = $osdev if !defined($rootdev); # simply point to first disk
1106 my $by_id = find_stable_path("/dev/disk/by-id", $devname);
1107 push @$bootdevinfo, { esp => $efidev, devname => $devname,
1108 osdev => $osdev, by_id => $by_id };
1109 push @$btrfs_partitions, $osdev;
1110 $disksize = $size;
5c06ced5 1111 }
c6ed3b24 1112
121ebc59
DM
1113 &$udevadm_trigger_block();
1114
1115 btrfs_create($btrfs_partitions, $btrfs_mode);
1116
c6ed3b24
DM
1117 } elsif ($use_zfs) {
1118
c6ed3b24
DM
1119 my ($devlist, $bootdevlist, $vdev) = get_zfs_raid_setup();
1120
1121 my $disksize;
1122 foreach my $hd (@$bootdevlist) {
1123 my $devname = @$hd[1];
f810f5d0
DM
1124 my ($size, $osdev) =
1125 partition_bootable_zfs_disk($devname);
14aacec8 1126 zfs_mirror_size_check($disksize, $size) if $disksize;
f810f5d0 1127 push @$bootdevinfo, { devname => $devname, osdev => $osdev};
c6ed3b24 1128 $disksize = $size;
c6ed3b24
DM
1129 }
1130
121ebc59 1131 &$udevadm_trigger_block();
c6ed3b24 1132
35c6f89c
DM
1133 foreach my $di (@$bootdevinfo) {
1134 my $devname = $di->{devname};
1135 $di->{by_id} = find_stable_path ("/dev/disk/by-id", $devname);
1136
1137 # Note: using /dev/disk/by-id/ does not work for unknown reason, we get
1138 # cannot create 'rpool': no such pool or dataset
1139 #my $osdev = find_stable_path ("/dev/disk/by-id", $di->{osdev}) || $di->{osdev};
c6ed3b24 1140
35c6f89c
DM
1141 my $osdev = $di->{osdev};
1142 $vdev =~ s/ $devname/ $osdev/;
1143 }
1144
5fd81672 1145 zfs_create_rpool($vdev);
35c6f89c 1146
c6ed3b24 1147 my $swap_size = compute_swapsize($disksize);
5fd81672 1148 $swapfile = zfs_create_swap($swap_size);
c6ed3b24
DM
1149
1150 } else {
1151
1152 die "target '$target_hd' is not a valid block device\n" if ! -b $target_hd;
1153
1154 my $maxhdsize;
1155 if ($config_options->{hdsize}) {
1156 # max hdsize passed on cmdline (GB)
1157 $maxhdsize = $config_options->{hdsize}*1024*1024;
1158 }
1159
1160 my ($os_size, $osdev, $efidev);
a2876e48 1161 ($os_size, $osdev, $efidev) =
121ebc59 1162 partition_bootable_disk($target_hd, $maxhdsize, '8E00');
c6ed3b24 1163
121ebc59 1164 &$udevadm_trigger_block();
c6ed3b24 1165
35c6f89c
DM
1166 my $by_id = find_stable_path ("/dev/disk/by-id", $target_hd);
1167 push @$bootdevinfo, { esp => $efidev, devname => $target_hd,
1168 osdev => $osdev, by_id => $by_id };
c6ed3b24 1169
35c6f89c
DM
1170 my $swap_size = compute_swapsize($os_size);
1171 ($rootdev, $datadev, $swapfile) =
1172 create_lvm_volumes($osdev, $os_size, $swap_size);
c6ed3b24 1173
35c6f89c 1174 # trigger udev to create /dev/disk/by-uuid
121ebc59 1175 &$udevadm_trigger_block(1);
89a12446
DM
1176 }
1177
481671c3
DM
1178 if ($use_zfs) {
1179 # to be fast during installation
1180 syscmd ("zfs set sync=disabled $zfspoolname") == 0 ||
1181 die "unable to set zfs properties\n";
1182 }
1183
89a12446
DM
1184 update_progress (0.03, 0, $maxper, "create swap space");
1185 if ($swapfile) {
7bc4f6bd 1186 syscmd ("mkswap -f $swapfile") == 0 ||
89a12446
DM
1187 die "unable to create swap space\n";
1188 }
1189
1190 update_progress (0.05, 0, $maxper, "creating filesystems");
1191
c6ed3b24 1192 foreach my $di (@$bootdevinfo) {
f810f5d0 1193 next if !$di->{esp};
c6ed3b24
DM
1194 syscmd ("mkfs.vfat -F32 $di->{esp}") == 0 ||
1195 die "unable to initialize EFI ESP on device $di->{esp}\n";
1196 }
1197
121ebc59
DM
1198 if ($use_zfs) {
1199 # do nothing
1200 } elsif ($use_btrfs) {
1201 # do nothing
1202 } else {
1203 create_filesystem ($rootdev, 'root', $filesys, 0.05, $maxper, 0, 1);
89a12446
DM
1204 }
1205
1206 update_progress (1, 0.05, $maxper, "mounting target $rootdev");
1207
121ebc59
DM
1208 if ($use_zfs) {
1209 # do nothing
121ebc59 1210 } else {
6e56032e
FG
1211 my $mount_opts = 'noatime';
1212 $mount_opts .= ',nobarrier'
1213 if $use_btrfs || $filesys =~ /^ext\d$/;
1214
1215 syscmd("mount -n $rootdev -o $mount_opts $targetdir") == 0 ||
35c6f89c
DM
1216 die "unable to mount $rootdev\n";
1217 }
89a12446 1218
35c6f89c
DM
1219 mkdir "$targetdir/boot";
1220 mkdir "$targetdir/boot/efi";
89a12446 1221
5fd81672
DM
1222 mkdir "$targetdir/var";
1223 mkdir "$targetdir/var/lib";
1224 mkdir "$targetdir/var/lib/vz";
121ebc59
DM
1225 mkdir "$targetdir/var/lib/pve";
1226
1227 if ($use_btrfs) {
1228 syscmd("btrfs subvolume create $targetdir/var/lib/pve/local-btrfs") == 0 ||
1229 die "unable to create btrfs subvolume\n";
1230 }
89a12446
DM
1231
1232 display_html ("extract2-rulesystem.htm");
1233 update_progress (1, 0.05, $maxper, "extracting base system");
1234
fafc616c
DM
1235 my ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size) = stat ($basefile);
1236 $ino || die "unable to open file '$basefile' - $!\n";
968fa90b 1237
89a12446
DM
1238 my $files;
1239 if ($opt_testmode) {
d2120e51 1240 $files = file_read_firstline("/pve/$release/install/pve-base.cnt");
89a12446 1241 } else {
fafc616c 1242 $files = file_read_firstline("/cdrom/proxmox/pve-base.cnt");
89a12446
DM
1243 }
1244
1245 my $per = 0;
1246 my $count = 0;
1247
fafc616c 1248 run_command ("unsquashfs -f -dest $targetdir -i $basefile", sub {
89a12446 1249 my $line = shift;
fafc616c 1250 return if $line !~ m/^$targetdir/;
89a12446
DM
1251 $count++;
1252 my $nper = int (($count *100)/$files);
1253 if ($nper != $per) {
1254 $per = $nper;
0f3d1edd 1255 my $frac = $per > 100 ? 1 : $per/100;
89a12446
DM
1256 update_progress ($frac, $maxper, 0.5);
1257 }
1258 });
1259
1260 syscmd ("mount -n -t tmpfs tmpfs $targetdir/tmp") == 0 ||
1261 die "unable to mount tmpfs on $targetdir/tmp\n";
1262 syscmd ("mount -n -t proc proc $targetdir/proc") == 0 ||
1263 die "unable to mount proc on $targetdir/proc\n";
1264 syscmd ("mount -n -t sysfs sysfs $targetdir/sys") == 0 ||
1265 die "unable to mount sysfs on $targetdir/sys\n";
1266
1267 display_html ("extract3-spam.htm");
1268 update_progress (1, $maxper, 0.5, "configuring base system");
1269
1270 # configure hosts
1271
968fa90b 1272 my $hosts =
89a12446 1273 "127.0.0.1 localhost.localdomain localhost\n" .
968fa90b 1274 "$ipaddress $hostname.$domain $hostname pvelocalhost\n\n" .
89a12446
DM
1275 "# The following lines are desirable for IPv6 capable hosts\n\n" .
1276 "::1 ip6-localhost ip6-loopback\n" .
1277 "fe00::0 ip6-localnet\n" .
1278 "ff00::0 ip6-mcastprefix\n" .
1279 "ff02::1 ip6-allnodes\n" .
1280 "ff02::2 ip6-allrouters\n" .
1281 "ff02::3 ip6-allhosts\n";
1282
968fa90b 1283 write_config ($hosts, "$targetdir/etc/hosts");
89a12446 1284
968fa90b 1285 write_config ("$hostname\n", "$targetdir/etc/hostname");
89a12446
DM
1286
1287 syscmd ("/bin/hostname $hostname") if !$opt_testmode;
1288
1289 # configure interfaces
1290
b6200603
DM
1291 my $ifaces = "auto lo\niface lo inet loopback\n\n";
1292
1293 my $ntype = $ipversion == 4 ? 'inet' : 'inet6';
1294
fe44bd92
FG
1295 my $bridge_port = $ipconf->{ifaces}->{$ipconf->{selected}}->{name};
1296
1297 $ifaces .= "iface $bridge_port $ntype manual\n";
1298
b6200603 1299 $ifaces .=
fe44bd92 1300 "\nauto vmbr0\niface vmbr0 $ntype static\n" .
b6200603
DM
1301 "\taddress $ipaddress\n" .
1302 "\tnetmask $netmask\n" .
1303 "\tgateway $gateway\n" .
fe44bd92 1304 "\tbridge_ports $bridge_port\n" .
b6200603
DM
1305 "\tbridge_stp off\n" .
1306 "\tbridge_fd 0\n";
89a12446 1307
fe44bd92
FG
1308 foreach my $iface (sort keys %{$ipconf->{ifaces}}) {
1309 my $name = $ipconf->{ifaces}->{$iface}->{name};
1310 next if $name eq $bridge_port;
1311
1312 $ifaces .= "\niface $name $ntype manual\n";
1313 }
1314
89a12446
DM
1315 write_config ($ifaces, "$targetdir/etc/network/interfaces");
1316
1317 # configure dns
1318
39a0f44b
FG
1319 my $resolvconf = "search $domain\nnameserver $dnsserver\n";
1320 write_config ($resolvconf, "$targetdir/etc/resolv.conf");
89a12446 1321
5c06ced5
DM
1322 # configure fstab
1323
1324 my $fstab = "# <file system> <mount point> <type> <options> <dump> <pass>\n";
1325
121ebc59
DM
1326 if ($use_zfs) {
1327 # do nothing
1328 } elsif ($use_btrfs) {
1329 my $fsuuid;
1330 my $cmd = "blkid -u filesystem -t TYPE=btrfs -o export $rootdev";
1331 run_command($cmd, sub {
1332 my $line = shift;
1333
1334 if ($line =~ m/^UUID=([A-Fa-f0-9\-]+)$/) {
1335 $fsuuid = $1;
1336 }
1337 });
1338
1339 die "unable to detect FS UUID" if !defined($fsuuid);
1340
1341 $fstab .= "UUID=$fsuuid / btrfs defaults 0 1\n";
1342 } else {
80090926
DM
1343 my $root_mountopt = $fssetup->{$filesys}->{root_mountopt} || 'defaults';
1344 $fstab .= "$rootdev / $filesys ${root_mountopt} 0 1\n";
7bc4f6bd 1345 }
a84ea010
DM
1346
1347 # mount /boot/efi
1348 # Note: this is required by current grub, but really dangerous, because
1349 # vfat does not have journaling, so it triggers manual fsck after each crash
1350 # so we only mount /boot/efi if really required (efi systems).
1351 if ($grub_plattform =~ m/^efi-/) {
1352 if (scalar(@$bootdevinfo)) {
f810f5d0
DM
1353 my $di = @$bootdevinfo[0]; # simply use first disk
1354 if ($di->{esp}) {
1355 my $efi_boot_uuid = $di->{esp};
1356 if (my $uuid = find_dev_by_uuid ($di->{esp})) {
1357 $efi_boot_uuid = "UUID=$uuid";
1358 }
84761f93 1359
f810f5d0
DM
1360 $fstab .= "${efi_boot_uuid} /boot/efi vfat defaults 0 1\n";
1361 }
a84ea010 1362 }
84761f93
DM
1363 }
1364
a84ea010 1365
89a12446
DM
1366 $fstab .= "$swapfile none swap sw 0 0\n" if $swapfile;
1367
1368 $fstab .= "proc /proc proc defaults 0 0\n";
1369
1370 write_config ($fstab, "$targetdir/etc/fstab");
1371 write_config ("", "$targetdir/etc/mtab");
968fa90b 1372
89a12446 1373 syscmd ("cp ${proxmox_dir}/policy-disable-rc.d " .
968fa90b 1374 "$targetdir/usr/sbin/policy-rc.d") == 0 ||
89a12446
DM
1375 die "unable to copy policy-rc.d\n";
1376 syscmd ("cp ${proxmox_dir}/fake-start-stop-daemon " .
968fa90b 1377 "$targetdir/sbin/") == 0 ||
89a12446
DM
1378 die "unable to copy start-stop-daemon\n";
1379
1380 diversion_add ($targetdir, "/sbin/start-stop-daemon", "/sbin/fake-start-stop-daemon");
1381 diversion_add ($targetdir, "/usr/sbin/update-grub", "/bin/true");
1382 diversion_add ($targetdir, "/usr/sbin/update-initramfs", "/bin/true");
1383
1384 syscmd ("touch $targetdir/proxmox_install_mode");
1385
e35d5efb 1386 my $grub_install_devices_txt = '';
3573c046 1387 foreach my $di (@$bootdevinfo) {
e35d5efb 1388 $grub_install_devices_txt .= ', ' if $grub_install_devices_txt;
ff863262 1389 $grub_install_devices_txt .= $di->{by_id} || $di->{devname};
3573c046
DM
1390 }
1391
814f5c39
DM
1392 # Note: debian-installer/locale is used by console-setup
1393
89a12446
DM
1394 debconfig_set ($targetdir, <<_EOD);
1395locales locales/default_environment_locale select en_US.UTF-8
1396locales locales/locales_to_be_generated select en_US.UTF-8 UTF-8
1397samba-common samba-common/dhcp boolean false
1398samba-common samba-common/workgroup string WORKGROUP
1399postfix postfix/main_mailer_type select Local only
814f5c39
DM
1400keyboard-configuration keyboard-configuration/xkb-keymap select $keymap
1401d-i debian-installer/locale select en_US.UTF-8
3573c046 1402grub-pc grub-pc/install_devices select $grub_install_devices_txt
89a12446
DM
1403_EOD
1404
fafc616c 1405 my $pkgdir = $opt_testmode ? "packages" : "/cdrom/proxmox/packages";
89a12446
DM
1406 my $pkg_count = 0;
1407 while (<$pkgdir/*.deb>) { $pkg_count++ };
1408
121ebc59
DM
1409 # btrfs/dpkg is extremely slow without --force-unsafe-io
1410 my $dpkg_opts = $use_btrfs ? "--force-unsafe-io" : "";
1411
89a12446
DM
1412 $count = 0;
1413 while (<$pkgdir/*.deb>) {
1414 chomp;
1415 my $path = $_;
1416 my ($deb) = $path =~ m/$pkgdir\/(.*\.deb)/;
1e61f3d8
DM
1417# if ($deb =~ m/^grub-efi-/ && $deb !~ m/^grub-${grub_plattform}/) {
1418# $count++;
1419# next;
1420# }
89a12446
DM
1421 update_progress ($count/$pkg_count, 0.5, 0.75, "extracting $deb");
1422 print "extracting: $deb\n";
1423 syscmd ("cp $path $targetdir/tmp/$deb") == 0 ||
1424 die "installation of package $deb failed\n";
121ebc59 1425 syscmd ("chroot $targetdir dpkg $dpkg_opts --force-depends --no-triggers --unpack /tmp/$deb") == 0 ||
968fa90b 1426 die "installation of package $deb failed\n";
89a12446
DM
1427 update_progress ((++$count)/$pkg_count, 0.5, 0.75);
1428 }
1429
1430 display_html ("extract4-virus.htm");
1431
121ebc59 1432 my $cmd = "chroot $targetdir dpkg $dpkg_opts --force-confold --configure -a";
89a12446
DM
1433 $count = 0;
1434 run_command ($cmd, sub {
1435 my $line = shift;
1436 if ($line =~ m/Setting up\s+(\S+)/) {
1437 update_progress ((++$count)/$pkg_count, 0.75, 0.95,
1438 "configuring $1");
1439 }
1440 });
968fa90b
DM
1441
1442 debconfig_set ($targetdir, <<_EOD);
89a12446
DM
1443postfix postfix/main_mailer_type select No configuration
1444_EOD
1445
1446 unlink "$targetdir/etc/mailname";
1447 $postfix_main_cf =~ s/__FQDN__/${hostname}.${domain}/;
1448 write_config ($postfix_main_cf, "$targetdir/etc/postfix/main.cf");
1449
1450 # make sure we have all postfix directories
1451 syscmd ("chroot $targetdir /usr/sbin/postfix check");
1452 # cleanup mail queue
1453 syscmd ("chroot $targetdir /usr/sbin/postsuper -d ALL");
1454
6b5dc3d0
DM
1455 # enable NTP (timedatectl set-ntp true does not work without DBUS)
1456 syscmd ("chroot $targetdir /bin/systemctl enable systemd-timesyncd.service");
1457
89a12446
DM
1458 unlink "$targetdir/proxmox_install_mode";
1459
968fa90b 1460 # set timezone
89a12446
DM
1461 unlink ("$targetdir/etc/localtime");
1462 symlink ("/usr/share/zoneinfo/$timezone", "$targetdir/etc/localtime");
1463 write_config ("$timezone\n", "$targetdir/etc/timezone");
1464
89a12446
DM
1465 # set apt mirror
1466 if (my $mirror = $cmap->{country}->{$country}->{mirror}) {
1467 my $fn = "$targetdir/etc/apt/sources.list";
968fa90b 1468 syscmd ("sed -i 's/ftp\\.debian\\.org/$mirror/' '$fn'");
89a12446
DM
1469 }
1470
19edf8b7
DM
1471 # create extended_states for apt (avoid cron job warning if that
1472 # file does not exist)
1473 write_config ('', "$targetdir/var/lib/apt/extended_states");
1474
c2657b8b
DM
1475 # allow ssh root login
1476 syscmd ("sed -i 's/^PermitRootLogin.*/PermitRootLogin yes/' '$targetdir/etc/ssh/sshd_config'");
1477
89a12446
DM
1478 # save installer settings
1479 my $ucc = uc ($country);
1480 debconfig_set ($targetdir, <<_EOD);
1481pve-manager pve-manager/country string $ucc
1482_EOD
1483
1484 update_progress (0.8, 0.95, 1, "make system bootable");
1485
5c06ced5 1486 if ($use_zfs) {
52f45831 1487 syscmd ("sed -i -e 's/^GRUB_CMDLINE_LINUX=.*/GRUB_CMDLINE_LINUX=\"root=ZFS=$zfspoolname\\/ROOT\\/pve-1 boot=zfs\"/' $targetdir/etc/default/grub") == 0 ||
5c06ced5
DM
1488 die "unable to update /etc/default/grub\n";
1489
1490 }
23c337f5 1491
89a12446
DM
1492 diversion_remove ($targetdir, "/usr/sbin/update-grub");
1493 diversion_remove ($targetdir, "/usr/sbin/update-initramfs");
1494
c6ed3b24 1495 if (!$opt_testmode) {
89a12446
DM
1496
1497 unlink ("$targetdir/etc/mtab");
1498 symlink ("/proc/mounts", "$targetdir/etc/mtab");
1499 syscmd ("mount -n --bind /dev $targetdir/dev");
1500
1501 syscmd ("chroot $targetdir /usr/sbin/update-initramfs -c -k $kapi") == 0 ||
1502 die "unable to install initramfs\n";
1503
c6ed3b24
DM
1504 foreach my $di (@$bootdevinfo) {
1505 my $dev = $di->{devname};
f810f5d0
DM
1506 syscmd ("chroot $targetdir /usr/sbin/grub-install --target i386-pc --no-floppy --bootloader-id='proxmox' $dev") == 0 ||
1507 die "unable to install the i386-pc boot loader on '$dev'\n";
1508
1509 if ($di->{esp}) {
1510 syscmd ("mount -n $di->{esp} $targetdir/boot/efi") == 0 ||
1511 die "unable to mount $di->{esp}\n";
5e0d6dce
FG
1512 my $rc = syscmd ("chroot $targetdir /usr/sbin/grub-install --target x86_64-efi --no-floppy --bootloader-id='proxmox' $dev");
1513 if ($rc != 0) {
1514 if (-d '/sys/firmware/efi') {
1515 die "unable to install the EFI boot loader on '$dev'\n";
1516 } else {
1517 warn "unable to install the EFI boot loader on '$dev', ignoring (not booted using UEFI)\n";
1518 }
1519 }
fd3ec787
DM
1520 # also install fallback boot file (OVMF does not boot without)
1521 mkdir("$targetdir/boot/efi/EFI/BOOT");
1522 syscmd("cp $targetdir/boot/efi/EFI/proxmox/grubx64.efi $targetdir/boot/efi/EFI/BOOT/BOOTx64.EFI") == 0 ||
1523 die "unable to copy efi boot loader\n";
1524
f810f5d0
DM
1525 syscmd ("umount $targetdir/boot/efi") == 0 ||
1526 die "unable to umount $targetdir/boot/efi\n";
1e61f3d8 1527 }
c6ed3b24 1528 }
89a12446
DM
1529
1530 syscmd ("chroot $targetdir /usr/sbin/update-grub") == 0 ||
c6ed3b24 1531 die "unable to update boot loader config\n";
89a12446
DM
1532
1533 syscmd ("umount $targetdir/dev");
1534 }
1535
968fa90b 1536 # cleanup
89a12446 1537
968fa90b 1538 # hack: remove dead.letter from sshd installation
89a12446
DM
1539 syscmd ("rm -rf $targetdir/dead.letter");
1540
89a12446
DM
1541 unlink "$targetdir/usr/sbin/policy-rc.d";
1542
1543 diversion_remove ($targetdir, "/sbin/start-stop-daemon");
1544
1545 # set root password
968fa90b 1546 my $octets = encode("utf-8", $password);
89a12446
DM
1547 run_command ("chroot $targetdir /usr/sbin/chpasswd", undef,
1548 "root:$octets\n");
7053f98b
DM
1549
1550 # create pmxcfs DB
1551
1552 my $tmpdir = "$targetdir/tmp/pve";
1553 mkdir $tmpdir;
1554
1555 # write vnc keymap to datacenter.cfg
1556 my $vnckmap = $cmap->{kmap}->{$keymap}->{kvm} || 'en-us';
968fa90b 1557 write_config ("keyboard: $vnckmap\n",
7053f98b
DM
1558 "$tmpdir/datacenter.cfg");
1559
1560 # save admin email
968fa90b 1561 write_config ("user:root\@pam:1:0:::${mailto}::\n",
7053f98b 1562 "$tmpdir/user.cfg");
968fa90b 1563
5fd81672
DM
1564 # write storage.cfg
1565 my $strorage_cfg_fn = "$tmpdir/storage.cfg";
1566 if ($use_zfs) {
1567 write_config ($storage_cfg_zfs, $strorage_cfg_fn);
121ebc59
DM
1568 } elsif ($use_btrfs) {
1569 write_config ($storage_cfg_btrfs, $strorage_cfg_fn);
5fd81672
DM
1570 } else {
1571 write_config ($storage_cfg_lvmthin, $strorage_cfg_fn);
1572 }
1573
7053f98b
DM
1574 run_command("chroot $targetdir /usr/bin/create_pmxcfs_db /tmp/pve /var/lib/pve-cluster/config.db");
1575
1576 syscmd ("rm -rf $tmpdir");
89a12446
DM
1577 };
1578
1579 my $err = $@;
1580
1581 update_progress (1, 0, 1, "");
1582
1583 print $err if $err;
1584
1585 if ($opt_testmode) {
121ebc59
DM
1586 my $elapsed = Time::HiRes::tv_interval($starttime);
1587 print "Elapsed extract time: $elapsed\n";
1588
89a12446
DM
1589 syscmd ("chroot $targetdir /usr/bin/dpkg-query -W --showformat='\${package}\n'> pve-final.pkglist");
1590 }
1591
89a12446
DM
1592 syscmd ("umount $targetdir/var/lib/vz");
1593 syscmd ("umount $targetdir/tmp");
1594 syscmd ("umount $targetdir/proc");
1595 syscmd ("umount $targetdir/sys");
6fbd1fb1
DM
1596
1597 if ($use_zfs) {
1598 syscmd ("zfs umount -a") == 0 ||
1599 die "unable to unmount zfs\n";
1600 } else {
1601 syscmd ("umount -d $targetdir");
1602 }
89a12446 1603
5c06ced5 1604 if (!$err && $use_zfs) {
481671c3
DM
1605 syscmd ("zfs set sync=standard $zfspoolname") == 0 ||
1606 die "unable to set zfs properties\n";
1607
5c06ced5
DM
1608 syscmd ("zfs set mountpoint=/ $zfspoolname/ROOT/pve-1") == 0 ||
1609 die "zfs set mountpoint failed\n";
1610
1611 syscmd ("zpool set bootfs=$zfspoolname/ROOT/pve-1 $zfspoolname") == 0 ||
1612 die "zfs set bootfs failed\n";
1613 }
1614
89a12446
DM
1615 die $err if $err;
1616}
1617
1618sub display_html {
1619 my ($filename) = @_;
1620
8a50920c
DM
1621 my $path = "${proxmox_dir}/html/$filename";
1622 my $url = "file://$path";
1623
1624 my $data = file_get_contents($path);
1625
1626 if ($filename eq 'license.htm') {
1627 my $licensefn = -f "/EULA" ? "/EULA" :
1628 $opt_testmode ? "./copyright" :
1629 "/usr/share/doc/pve-installer/copyright";
f91c161b
DM
1630 my $license = decode('utf8', file_get_contents($licensefn));
1631 $license =~ m/^\s+(.+\n)/;
1632 my $title = $licensefn eq '/EULA' ? "END USER LICENSE AGREEMENT (EULA)": $1;
1633 $data =~ s/__LICENSE__/$license/;
8a50920c
DM
1634 $data =~ s/__LICENSE_TITLE__/$title/;
1635 }
1636
1637 $htmlview->load_html_string($data, $url);
7becc472
DM
1638}
1639
89a12446
DM
1640sub set_next {
1641 my ($text, $fctn) = @_;
1642
1643 $next_fctn = $fctn;
1644 $text = "_Next" if !$text;
1645 $next->set_label ($text);
968fa90b 1646
89a12446
DM
1647 $next->grab_focus ();
1648}
1649
1650sub url_requested {
1651 my ($doc, $url, $stream) = @_;
968fa90b 1652
89a12446
DM
1653 $stream->set_cancel_func (sub {}); # hack: avoid warning
1654
1655 my $path = "${proxmox_dir}/html/$url";
1656
1657 if (-f $path) {
1658 open (HTMLTMP, $path) ||
eb4b1e56 1659 die "unable to open file '$path' - $!\n";
89a12446
DM
1660 my $buf;
1661 while (my $i = read (HTMLTMP, $buf, 4096)) {
968fa90b 1662 $stream->write ($buf);
d2120e51 1663 Gtk3::main_iteration() while Gtk3::events_pending();
89a12446
DM
1664 }
1665 close (HTMLTMP);
1666 }
1667
1668 #$stream->close(); # hack: dont close - avoid crash
1669}
1670
1671sub create_main_window {
1672
7becc472 1673 $window = Gtk3::Window->new ();
89a12446 1674 $window->set_default_size (1024, 768);
84761f93 1675 $window->set_has_resize_grip(0);
89a12446
DM
1676 $window->set_decorated (0) if !$opt_testmode;
1677
7becc472 1678 my $vbox = Gtk3::VBox->new (0, 0);
89a12446 1679
709d22e0 1680 my $image = Gtk3::Image->new_from_file ("${proxmox_dir}/proxlogo.png");
89a12446
DM
1681 $vbox->pack_start ($image, 0, 0, 0);
1682
7becc472 1683 my $hbox = Gtk3::HBox->new (0, 0);
89a12446
DM
1684 $vbox->pack_start ($hbox, 1, 1, 0);
1685
7becc472
DM
1686 # my $f1 = Gtk3::Frame->new ('test');
1687 # $f1->set_shadow_type ('none');
1688 # $hbox->pack_start ($f1, 1, 1, 0);
89a12446 1689
7becc472 1690 my $sep1 = Gtk3::HSeparator->new;
89a12446
DM
1691 $vbox->pack_start ($sep1, 0, 0, 0);
1692
7becc472 1693 $cmdbox = Gtk3::HBox->new ();
89a12446
DM
1694 $vbox->pack_start ($cmdbox, 0, 0, 10);
1695
7becc472 1696 $next = Gtk3::Button->new ('_Next');
89a12446
DM
1697 $next->signal_connect (clicked => sub { &$next_fctn (); });
1698 $cmdbox->pack_end ($next, 0, 0, 10);
7becc472
DM
1699 my $abort = Gtk3::Button->new ('_Abort');
1700 $abort->set_can_focus (0);
89a12446
DM
1701 $cmdbox->pack_start ($abort, 0, 0, 10);
1702 $abort->signal_connect (clicked => sub { exit (-1); });
1703
7becc472
DM
1704 my $vbox2 = Gtk3::VBox->new (0, 0);
1705 $hbox->add ($vbox2);
89a12446 1706
7becc472
DM
1707 $htmlview = Gtk3::WebKit::WebView->new();
1708 my $scrolls = Gtk3::ScrolledWindow->new();
1709 $scrolls->add($htmlview);
1710
1711 my $hbox2 = Gtk3::HBox->new (0, 0);
1712 $hbox2->pack_start ($scrolls, 1, 1, 0);
89a12446
DM
1713
1714 $vbox2->pack_start ($hbox2, 1, 1, 0);
1715
7becc472 1716 my $vbox3 = Gtk3::VBox->new (0, 0);
89a12446
DM
1717 $vbox2->pack_start ($vbox3, 0, 0, 0);
1718
7becc472 1719 my $sep2 = Gtk3::HSeparator->new;
89a12446
DM
1720 $vbox3->pack_start ($sep2, 0, 0, 0);
1721
7becc472 1722 $inbox = Gtk3::HBox->new (0, 0);
89a12446
DM
1723 $vbox3->pack_start ($inbox, 0, 0, 0);
1724
1725 $window->add ($vbox);
1726
1727 $window->show_all;
1728 $window->realize ();
1729}
1730
d2120e51
DM
1731sub cleanup_view {
1732 $inbox->foreach(sub {
1733 my $child = shift;
1734 $inbox->remove ($child);
1735 });
89a12446
DM
1736}
1737
aed81ff0
DM
1738# fixme: newer GTK3 has special properties to handle numbers with Entry
1739# only allow floating point numbers with Gtk3::Entry
e73c5fcf 1740
aed81ff0
DM
1741sub check_float {
1742 my ($entry, $event) = @_;
1743
e73c5fcf
FG
1744 return check_number($entry, $event, 1);
1745}
1746
1747sub check_int {
1748 my ($entry, $event) = @_;
1749
1750 return check_number($entry, $event, 0);
1751}
1752
1753sub check_number {
1754 my ($entry, $event, $float) = @_;
aed81ff0
DM
1755
1756 my $val = $event->get_keyval;
1757
e73c5fcf 1758 if (($float && $val == ord '.') ||
aed81ff0
DM
1759 $val == Gtk3::Gdk::KEY_ISO_Left_Tab ||
1760 $val == Gtk3::Gdk::KEY_Shift_L ||
1761 $val == Gtk3::Gdk::KEY_Tab ||
1762 $val == Gtk3::Gdk::KEY_Left ||
1763 $val == Gtk3::Gdk::KEY_Right ||
1764 $val == Gtk3::Gdk::KEY_BackSpace ||
1765 $val == Gtk3::Gdk::KEY_Delete ||
1766 ($val >= ord '0' && $val <= ord '9') ||
1767 ($val >= Gtk3::Gdk::KEY_KP_0 &&
1768 $val <= Gtk3::Gdk::KEY_KP_9)) {
1769 return undef;
1770 }
1771
1772 return 1;
1773}
1774
d2120e51 1775sub create_text_input {
89a12446
DM
1776 my ($default, $text) = @_;
1777
7becc472 1778 my $hbox = Gtk3::HBox->new (0, 0);
89a12446 1779
7becc472 1780 my $label = Gtk3::Label->new ($text);
89a12446
DM
1781 $label->set_size_request (150, -1);
1782 $label->set_alignment (1, 0.5);
1783 $hbox->pack_start ($label, 0, 0, 10);
7becc472 1784 my $e1 = Gtk3::Entry->new ();
89a12446
DM
1785 $e1->set_width_chars (30);
1786 $hbox->pack_start ($e1, 0, 0, 0);
1787 $e1->set_text ($default);
1788
1789 return ($hbox, $e1);
1790}
1791
89a12446
DM
1792sub get_ip_config {
1793
fe44bd92
FG
1794 my $ifaces = {};
1795 my $default;
89a12446 1796
fe44bd92
FG
1797 my $links = `ip -o l`;
1798 foreach my $l (split /\n/,$links) {
1799 my ($index, $name, $flags, $state, $mac) = $l =~ m/^(\d+):\s+(\S+):\s+<(\S+)>.*\s+state\s+(\S+)\s+.*\s+link\/ether\s+(\S+)\s+/;
1800 next if !$name || $name eq 'lo';
89a12446 1801
fe44bd92
FG
1802 my $driver = readlink "/sys/class/net/$name/device/driver" || 'unknown';
1803 $driver =~ s!^.*/!!;
1804
1805 $ifaces->{"$index"} = {
1806 name => $name,
1807 driver => $driver,
1808 flags => $flags,
1809 state => $state,
1810 mac => $mac,
1811 };
1812
1813 my $addresses = `ip -o a s $name`;
1814 foreach my $a (split /\n/,$addresses) {
1815 my ($family, $ip, $prefix) = $a =~ m/^\Q$index\E:\s+\Q$name\E\s+(inet|inet6)\s+($IPRE)\/(\d+)\s+/;
1816 next if !$ip;
1817
1818 my $mask = $prefix;
1819
1820 if ($family eq 'inet') {
1821 next if !$ip =~ /$IPV4RE/;
1822 next if $prefix < 8 || $prefix > 32;
1823 $mask = @$ipv4_reverse_mask[$prefix];
1824 } else {
1825 next if !$ip =~ /$IPV6RE/;
1826 }
1827
1828 $default = $index if !$default;
1829
1830 $ifaces->{"$index"}->{"$family"} = {
1831 mask => $mask,
1832 addr => $ip,
1833 };
1834 }
1835 }
1836
1837
1838 my $route = `ip route`;
1839 my ($gateway) = $route =~ m/^default\s+via\s+(\S+)\s+/m;
89a12446
DM
1840
1841 my $resolvconf = `cat /etc/resolv.conf`;
1842 my ($dnsserver) = $resolvconf =~ m/^nameserver\s+(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})$/m;
713790a4 1843 my ($domain) = $resolvconf =~ m/^domain\s+(\S+)$/m;
89a12446
DM
1844
1845 return {
fe44bd92
FG
1846 default => $default,
1847 ifaces => $ifaces,
89a12446
DM
1848 gateway => $gateway,
1849 dnsserver => $dnsserver,
713790a4 1850 domain => $domain,
89a12446
DM
1851 }
1852}
1853
1854sub display_message {
1855 my ($msg) = @_;
1856
7becc472 1857 my $dialog = Gtk3::MessageDialog->new ($window, 'modal',
89a12446
DM
1858 'info', 'ok', $msg);
1859 $dialog->run();
1860 $dialog->destroy();
1861}
1862
1863sub display_error {
1864 my ($msg) = @_;
1865
7becc472 1866 my $dialog = Gtk3::MessageDialog->new ($window, 'modal',
89a12446
DM
1867 'error', 'ok', $msg);
1868 $dialog->run();
1869 $dialog->destroy();
1870}
1871
fe44bd92
FG
1872my $ipconf_first_view = 1;
1873
89a12446
DM
1874sub create_ipconf_view {
1875
1876 cleanup_view ();
1877 display_html ("ipconf.htm");
1878
7becc472 1879 my $vbox = Gtk3::VBox->new (0, 0);
89a12446 1880 $inbox->pack_start ($vbox, 1, 0, 0);
7becc472 1881 my $hbox = Gtk3::HBox->new (0, 0);
53986d77 1882 $vbox->pack_start ($hbox, 0, 0, 10);
7becc472 1883 my $vbox2 = Gtk3::VBox->new (0, 0);
89a12446
DM
1884 $hbox->add ($vbox2);
1885
fe44bd92
FG
1886 my $ipbox;
1887 ($ipbox, $ipconf_entry_addr) =
1888 create_text_input ("192.168.100.2", 'IP Address:');
1889
1890 my $maskbox;
1891 ($maskbox, $ipconf_entry_mask) =
1892 create_text_input ("255.255.255.0", 'Netmask:');
1893
1894 my $device_cb = Gtk3::ComboBoxText->new();
1895 $device_cb->set_active(0);
1896 $device_cb->set_visible(1);
1897
1898 my $get_device_desc = sub {
1899 my $iface = shift;
1900 return "$iface->{name} - $iface->{mac} ($iface->{driver})";
1901 };
1902
1903 my $device_active_map = {};
5b6ba737
FG
1904
1905 my $device_change_handler = sub {
1906 my $current = shift;
1907 $ipconf->{selected} = $device_active_map->{$current->get_active()};
1908 my $iface = $ipconf->{ifaces}->{$ipconf->{selected}};
1909 $ipconf_entry_addr->set_text($iface->{inet}->{addr} || $iface->{inet6}->{addr})
1910 if $iface->{inet}->{addr} || $iface->{inet6}->{addr};
1911 $ipconf_entry_mask->set_text($iface->{inet}->{mask} || $iface->{inet6}->{mask})
1912 if $iface->{inet}->{mask} || $iface->{inet6}->{mask};
1913 };
1914
fe44bd92
FG
1915 my $i = 0;
1916 foreach my $index (sort keys %{$ipconf->{ifaces}}) {
1917 $device_cb->append_text(&$get_device_desc($ipconf->{ifaces}->{$index}));
1918 $device_active_map->{$i} = $index;
1919 if ($ipconf_first_view && $index == $ipconf->{default}) {
1920 $device_cb->set_active($i);
5b6ba737 1921 &$device_change_handler($device_cb);
fe44bd92
FG
1922 $ipconf_first_view = 0;
1923 }
5b6ba737 1924 $device_cb->signal_connect ('changed' => $device_change_handler);
fe44bd92
FG
1925 $i++;
1926 }
1927
5b6ba737
FG
1928 $device_cb->set_active(0)
1929 if !($ipconf->{selected});
1930
fe44bd92
FG
1931 my $devicebox = Gtk3::HBox->new (0, 0);
1932 my $label = Gtk3::Label->new ("Management Interface:");
1933 $label->set_size_request (150, -1);
1934 $label->set_alignment (1, 0.5);
1935 $devicebox->pack_start ($label, 0, 0, 10);
1936 $devicebox->pack_start ($device_cb, 0, 0, 0);
1937
1938 $vbox2->pack_start ($devicebox, 0, 0, 2);
968fa90b 1939
a39bc1f2 1940 my $hn = $ipconf->{domain} ? "pve.$ipconf->{domain}" : 'pve.example.invalid';
713790a4 1941
968fa90b 1942 my ($hostbox, $hostentry) =
d2120e51 1943 create_text_input ($hn, 'Hostname (FQDN):');
89a12446
DM
1944 $vbox2->pack_start ($hostbox, 0, 0, 2);
1945
89a12446
DM
1946 $vbox2->pack_start ($ipbox, 0, 0, 2);
1947
89a12446
DM
1948 $vbox2->pack_start ($maskbox, 0, 0, 2);
1949
1950 $gateway = $ipconf->{gateway} || '192.168.100.1';
1951
1952 my $gwbox;
d2120e51
DM
1953 ($gwbox, $ipconf_entry_gw) =
1954 create_text_input ($gateway, 'Gateway:');
89a12446 1955
53986d77 1956 $vbox2->pack_start ($gwbox, 0, 0, 2);
89a12446
DM
1957
1958 $dnsserver = $ipconf->{dnsserver} || $gateway;
1959
1960 my $dnsbox;
d2120e51
DM
1961 ($dnsbox, $ipconf_entry_dns) =
1962 create_text_input ($dnsserver, 'DNS Server:');
89a12446
DM
1963
1964 $vbox2->pack_start ($dnsbox, 0, 0, 0);
1965
1966 $inbox->show_all;
968fa90b 1967 set_next (undef, sub {
d2120e51
DM
1968
1969 # verify hostname
1970
89a12446 1971 my $text = $hostentry->get_text();
968fa90b 1972
89a12446
DM
1973 $text =~ s/^\s+//;
1974 $text =~ s/\s+$//;
1975
ac3757a9 1976 my $namere = "([a-zA-Z0-9]([a-zA-Z0-9\-]*[a-zA-Z0-9])?)";
968fa90b 1977
24973868
WB
1978 # Debian does not support purely numeric hostnames
1979 if ($text && $text =~ /^[0-9]+(?:\.|$)/) {
1980 display_message("Purely numeric hostnames are not allowed.");
1981 $hostentry->grab_focus();
1982 return;
1983 }
1984
a39bc1f2 1985 if ($text && $text =~ m/^(${namere}\.)*${namere}$/ && $text !~ m/.example.invalid$/ &&
89a12446
DM
1986 $text =~ m/^([^\.]+)\.(\S+)$/) {
1987 $hostname = $1;
1988 $domain = $2;
d2120e51
DM
1989 } else {
1990 display_message ("Hostname does not look like a fully qualified domain name.");
1991 $hostentry->grab_focus();
89a12446
DM
1992 return;
1993 }
d2120e51
DM
1994
1995 # verify ip address
1996
1997 $text = $ipconf_entry_addr->get_text();
1998 $text =~ s/^\s+//;
1999 $text =~ s/\s+$//;
2000 if ($text =~ m!^($IPV4RE)$!) {
2001 $ipaddress = $text;
b6200603
DM
2002 $ipversion = 4;
2003 } elsif ($text =~ m!^($IPV6RE)$!) {
2004 $ipaddress = $text;
2005 $ipversion = 6;
d2120e51
DM
2006 } else {
2007 display_message ("IP address is not valid.");
2008 $ipconf_entry_addr->grab_focus();
2009 return;
2010 }
2011
2012 $text = $ipconf_entry_mask->get_text();
2013 $text =~ s/^\s+//;
2014 $text =~ s/\s+$//;
b6200603
DM
2015 if (($ipversion == 6) && ($text =~ m/^(\d+)$/) && ($1 >= 8) && ($1 <= 126)) {
2016 $netmask = $text;
2017 } elsif (($ipversion == 4) && defined($ipv4_mask_hash->{$text})) {
d2120e51
DM
2018 $netmask = $text;
2019 } else {
2020 display_message ("Netmask is not valid.");
2021 $ipconf_entry_mask->grab_focus();
2022 return;
2023 }
2024
2025 $text = $ipconf_entry_gw->get_text();
2026 $text =~ s/^\s+//;
2027 $text =~ s/\s+$//;
b6200603
DM
2028 if (($ipversion == 4) && ($text =~ m!^($IPV4RE)$!)) {
2029 $gateway = $text;
2030 } elsif (($ipversion == 6) && ($text =~ m!^($IPV6RE)$!)) {
d2120e51
DM
2031 $gateway = $text;
2032 } else {
2033 display_message ("Gateway is not valid.");
2034 $ipconf_entry_gw->grab_focus();
2035 return;
2036 }
2037
2038 $text = $ipconf_entry_dns->get_text();
2039 $text =~ s/^\s+//;
2040 $text =~ s/\s+$//;
b6200603
DM
2041 if (($ipversion == 4) && ($text =~ m!^($IPV4RE)$!)) {
2042 $dnsserver = $text;
2043 } elsif (($ipversion == 6) && ($text =~ m!^($IPV6RE)$!)) {
d2120e51
DM
2044 $dnsserver = $text;
2045 } else {
2046 display_message ("DNS server is not valid.");
2047 $ipconf_entry_dns->grab_focus();
2048 return;
2049 }
2050
2051 #print "TEST $ipaddress $netmask $gateway $dnsserver\n";
2052
2053 create_extract_view ();
89a12446
DM
2054 });
2055
2056 $hostentry->grab_focus();
2057}
2058
2059sub get_device_desc {
2060 my ($devname, $size, $model) = @_;
2061
d2120e51 2062 if ($size && ($size > 0)) {
1bd457bb 2063 $size = int($size/2048); # size in MB, from 512B "sectors"
89a12446 2064
d2120e51 2065 my $text = "$devname (";
89a12446
DM
2066 if ($size >= 1024) {
2067 $size = int($size/1024); # size in GB
d2120e51 2068 $text .= "${size}GB";
89a12446 2069 } else {
d2120e51 2070 $text .= "${size}MB";
89a12446
DM
2071 }
2072
d2120e51
DM
2073 $text .= ", $model" if $model;
2074 $text .= ")";
2075
89a12446
DM
2076 } else {
2077 return $devname;
2078 }
2079}
2080
2081sub update_layout {
2082 my ($cb, $kmap) = @_;
2083
2084 my $ind;
2085 my $def;
2086 my $i = 0;
2087 my $kmaphash = $cmap->{kmaphash};
2088 foreach my $layout (sort keys %$kmaphash) {
2089 $def = $i if $kmaphash->{$layout} eq 'en-us';
2090 $ind = $i if $kmap && $kmaphash->{$layout} eq $kmap;
2091 $i++;
2092 }
2093
2094 $cb->set_active ($ind || $def || 0);
2095}
2096
2097my $lastzonecb;
2098sub update_zonelist {
2099 my ($box, $cc) = @_;
2100
2101 my $cczones = $cmap->{cczones};
2102 my $zones = $cmap->{zones};
2103
2104 my $sel;
2105 if ($lastzonecb) {
2106 $sel = $lastzonecb->get_active_text();
2107 $box->remove ($lastzonecb);
2108 } else {
2109 $sel = $timezone; # used once to select default
2110 }
2111
bcbfab6b 2112 my $cb = $lastzonecb = Gtk3::ComboBoxText->new();
89a12446
DM
2113 $cb->set_size_request (200, -1);
2114
2115 $cb->signal_connect ('changed' => sub {
2116 $timezone = $cb->get_active_text();
2117 });
2118
2119 my @za;
2120 if ($cc && defined ($cczones->{$cc})) {
2121 @za = keys %{$cczones->{$cc}};
2122 } else {
2123 @za = keys %$zones;
2124 }
2125 my $ind;
2126 my $i = 0;
2127 foreach my $zone (sort @za) {
2128 $ind = $i if $sel && $zone eq $sel;
2129 $cb->append_text ($zone);
2130 $i++;
2131 }
2132
2133 $cb->set_active ($ind || 0);
2134
2135 $cb->show;
2136 $box->pack_start ($cb, 0, 0, 0);
2137}
2138
2139sub create_password_view {
2140
2141 cleanup_view ();
2142
7becc472 2143 my $vbox2 = Gtk3::VBox->new (0, 0);
89a12446 2144 $inbox->pack_start ($vbox2, 1, 0, 0);
7becc472 2145 my $vbox = Gtk3::VBox->new (0, 0);
53986d77 2146 $vbox2->pack_start ($vbox, 0, 0, 10);
89a12446 2147
7becc472
DM
2148 my $hbox1 = Gtk3::HBox->new (0, 0);
2149 my $label = Gtk3::Label->new ("Password");
89a12446
DM
2150 $label->set_size_request (150, -1);
2151 $label->set_alignment (1, 0.5);
2152 $hbox1->pack_start ($label, 0, 0, 10);
7becc472 2153 my $pwe1 = Gtk3::Entry->new ();
89a12446
DM
2154 $pwe1->set_visibility (0);
2155 $pwe1->set_size_request (200, -1);
2156 $hbox1->pack_start ($pwe1, 0, 0, 0);
2157
7becc472
DM
2158 my $hbox2 = Gtk3::HBox->new (0, 0);
2159 $label = Gtk3::Label->new ("Confirm");
89a12446
DM
2160 $label->set_size_request (150, -1);
2161 $label->set_alignment (1, 0.5);
2162 $hbox2->pack_start ($label, 0, 0, 10);
7becc472 2163 my $pwe2 = Gtk3::Entry->new ();
89a12446
DM
2164 $pwe2->set_visibility (0);
2165 $pwe2->set_size_request (200, -1);
2166 $hbox2->pack_start ($pwe2, 0, 0, 0);
2167
7becc472
DM
2168 my $hbox3 = Gtk3::HBox->new (0, 0);
2169 $label = Gtk3::Label->new ("E-Mail");
89a12446
DM
2170 $label->set_size_request (150, -1);
2171 $label->set_alignment (1, 0.5);
2172 $hbox3->pack_start ($label, 0, 0, 10);
7becc472 2173 my $eme = Gtk3::Entry->new ();
89a12446 2174 $eme->set_size_request (200, -1);
a39bc1f2 2175 $eme->set_text('mail@example.invalid');
89a12446
DM
2176 $hbox3->pack_start ($eme, 0, 0, 0);
2177
2178
2179 $vbox->pack_start ($hbox1, 0, 0, 5);
2180 $vbox->pack_start ($hbox2, 0, 0, 5);
2181 $vbox->pack_start ($hbox3, 0, 0, 15);
2182
2183 $inbox->show_all;
2184
2185 display_html ("passwd.htm");
2186
2187 set_next (undef, sub {
2188
2189 my $t1 = $pwe1->get_text;
2190 my $t2 = $pwe2->get_text;
2191
2192 if (length ($t1) < 5) {
2193 display_message ("Password is too short.");
2194 $pwe1->grab_focus();
2195 return;
2196 }
2197
2198 if ($t1 ne $t2) {
2199 display_message ("Password does not match.");
2200 $pwe1->grab_focus();
2201 return;
2202 }
2203
2204 my $t3 = $eme->get_text;
2205 if ($t3 !~ m/^\S+\@\S+\.\S+$/) {
034f75e4 2206 display_message ("E-Mail does not look like a valid address" .
89a12446
DM
2207 " (user\@domain.tld)");
2208 $eme->grab_focus();
2209 return;
a39bc1f2 2210 }
89a12446 2211
a39bc1f2
FG
2212 if ($t3 eq 'mail@example.invalid') {
2213 display_message ("Please enter a valid E-Mail address");
2214 $eme->grab_focus();
2215 return;
89a12446
DM
2216 }
2217
2218 $password = $t1;
2219 $mailto = $t3;
2220
2221 create_ipconf_view();
2222 });
2223
2224 $pwe1->grab_focus();
2225
2226}
2227
2228sub create_country_view {
2229
2230 cleanup_view ();
2231
2232 my $countryhash = $cmap->{countryhash};
2233 my $ctr = $cmap->{country};
2234
7becc472 2235 my $vbox2 = Gtk3::VBox->new (0, 0);
89a12446 2236 $inbox->pack_start ($vbox2, 1, 0, 0);
7becc472 2237 my $vbox = Gtk3::VBox->new (0, 0);
53986d77 2238 $vbox2->pack_start ($vbox, 0, 0, 10);
89a12446 2239
7becc472 2240 my $w = Gtk3::Entry->new ();
89a12446
DM
2241 $w->set_size_request (200, -1);
2242
7becc472 2243 my $c = Gtk3::EntryCompletion->new ();
89a12446
DM
2244 $c->set_text_column (0);
2245 $c->set_minimum_key_length(0);
2246 $c->set_popup_set_width (1);
4443aa27 2247 $c->set_inline_completion (1);
89a12446 2248
7becc472
DM
2249 my $hbox2 = Gtk3::HBox->new (0, 0);
2250 my $label = Gtk3::Label->new ("Time zone");
89a12446
DM
2251 $label->set_size_request (150, -1);
2252 $label->set_alignment (1, 0.5);
2253 $hbox2->pack_start ($label, 0, 0, 10);
2254 update_zonelist ($hbox2);
2255
7becc472
DM
2256 my $hbox3 = Gtk3::HBox->new (0, 0);
2257 $label = Gtk3::Label->new ("Keyboard Layout");
89a12446
DM
2258 $label->set_size_request (150, -1);
2259 $label->set_alignment (1, 0.5);
2260 $hbox3->pack_start ($label, 0, 0, 10);
2261
bcbfab6b 2262 my $kmapcb = Gtk3::ComboBoxText->new();
89a12446
DM
2263 $kmapcb->set_size_request (200, -1);
2264 foreach my $layout (sort keys %{$cmap->{kmaphash}}) {
2265 $kmapcb->append_text ($layout);
2266 }
2267
2268 update_layout ($kmapcb);
2269 $hbox3->pack_start ($kmapcb, 0, 0, 0);
2270
2271 $kmapcb->signal_connect ('changed' => sub {
2272 my $sel = $kmapcb->get_active_text();
2273 if (my $kmap = $cmap->{kmaphash}->{$sel}) {
2274 my $xkmap = $cmap->{kmap}->{$kmap}->{x11};
2275 my $xvar = $cmap->{kmap}->{$kmap}->{x11var};
2276 syscmd ("setxkbmap $xkmap $xvar") if !$opt_testmode;
2277 $keymap = $kmap;
2278 }
2279 });
2280
2281 $w->signal_connect ('changed' => sub {
2282 my ($entry, $event) = @_;
2283 my $text = $entry->get_text;
2284
2285 if (my $cc = $countryhash->{lc($text)}) {
2286 update_zonelist ($hbox2, $cc);
2287 my $kmap = $ctr->{$cc}->{kmap} || 'en-us';
2288 update_layout ($kmapcb, $kmap);
2289 }
2290 });
2291
2292 $w->signal_connect (key_press_event => sub {
2293 my ($entry, $event) = @_;
2294 my $text = $entry->get_text;
2295
7becc472
DM
2296 my $val = $event->get_keyval;
2297
2298 if ($val == Gtk3::Gdk::KEY_Tab) {
89a12446 2299 my $cc = $countryhash->{lc($text)};
4443aa27 2300
89a12446
DM
2301 my $found = 0;
2302 my $compl;
7becc472 2303
4443aa27
DM
2304 if ($cc) {
2305 $found = 1;
2306 $compl = $ctr->{$cc}->{name};
2307 } else {
2308 foreach my $cc (keys %$ctr) {
2309 my $ct = $ctr->{$cc}->{name};
2310 if ($ct =~ m/^\Q$text\E.*$/i) {
2311 $found++;
2312 $compl = $ct;
2313 }
2314 last if $found > 1;
89a12446 2315 }
89a12446 2316 }
4443aa27 2317
89a12446 2318 if ($found == 1) {
7becc472 2319 $entry->set_text($compl);
3df718ea 2320 $c->complete();
89a12446
DM
2321 return undef;
2322 } else {
7becc472
DM
2323 #Gtk3::Gdk::beep();
2324 print chr(7); # beep ?
89a12446
DM
2325 }
2326
3df718ea
DM
2327 $c->complete();
2328
7becc472
DM
2329 my $buf = $w->get_buffer();
2330 $buf->insert_text(-1, '', -1); # popup selection
2331
89a12446
DM
2332 return 1;
2333 }
2334
2335 return undef;
2336 });
7becc472
DM
2337
2338 my $ls = Gtk3::ListStore->new('Glib::String');
89a12446
DM
2339 foreach my $cc (sort {$ctr->{$a}->{name} cmp $ctr->{$b}->{name} } keys %$ctr) {
2340 my $iter = $ls->append();
2341 $ls->set ($iter, 0, $ctr->{$cc}->{name});
2342 }
2343 $c->set_model ($ls);
2344
968fa90b 2345 $w->set_completion ($c);
89a12446 2346
7becc472 2347 my $hbox = Gtk3::HBox->new (0, 0);
89a12446 2348
7becc472 2349 $label = Gtk3::Label->new ("Country");
89a12446
DM
2350 $label->set_alignment (1, 0.5);
2351 $label->set_size_request (150, -1);
2352 $hbox->pack_start ($label, 0, 0, 10);
2353 $hbox->pack_start ($w, 0, 0, 0);
2354
2355 $vbox->pack_start ($hbox, 0, 0, 5);
2356 $vbox->pack_start ($hbox2, 0, 0, 5);
2357 $vbox->pack_start ($hbox3, 0, 0, 5);
2358
2359 if ($country) {
2360 $w->set_text ($ctr->{$country}->{name});
2361 }
2362
2363 $inbox->show_all;
2364
2365 display_html ("country.htm");
2366 set_next (undef, sub {
2367
2368 my $text = $w->get_text;
2369
2370 if (my $cc = $countryhash->{lc($text)}) {
2371 $country = $cc;
2372 create_password_view();
2373 return;
2374 } else {
2375 display_message ("Please select a country first.");
2376 $w->grab_focus();
2377 }
2378 });
2379
2380 $w->grab_focus();
2381}
2382
c6ed3b24
DM
2383my $target_hd_combo;
2384my $target_hd_label;
2385
2386my $hdopion_first_setup = 1;
2387
c7779156
FG
2388my $create_basic_grid = sub {
2389 my $grid = Gtk3::Grid->new();
2390 $grid->set_visible(1);
2391 $grid->set_column_spacing(10);
2392 $grid->set_row_spacing(10);
2393 $grid->set_hexpand(1);
2394
2395 $grid->set_margin_start(5);
2396 $grid->set_margin_end(5);
2397 $grid->set_margin_top(5);
2398 $grid->set_margin_bottom(5);
2399
2400 return $grid;
2401};
2402
2403my $create_label_widget_grid = sub {
2404 my ($labeled_widgets) = @_;
2405
2406 my $grid = &$create_basic_grid();
2407 my $row = 0;
2408
2409 for (my $i = 0; $i < @$labeled_widgets; $i += 2) {
2410 my $widget = @$labeled_widgets[$i+1];
2411 my $label = Gtk3::Label->new(@$labeled_widgets[$i]);
2412 $label->set_visible(1);
2413 $label->set_alignment (1, 0.5);
2414 $grid->attach($label, 0, $row, 1, 1);
2415 $widget->set_visible(1);
2416 $grid->attach($widget, 1, $row, 1, 1);
2417 $row++;
2418 }
2419
2420 return $grid;
2421};
2422
2423my $create_raid_disk_grid = sub {
2424 my $disk_labeled_widgets = [];
2425 for (my $i = 0; $i < @$hds; $i++) {
2426 my $disk_selector = Gtk3::ComboBoxText->new();
2427 $disk_selector->append_text("-- do not use --");
2428 $disk_selector->set_active(0);
2429 $disk_selector->set_visible(1);
2430 foreach my $hd (@$hds) {
2431 my ($disk, $devname, $size, $model) = @$hd;
2432 $disk_selector->append_text(get_device_desc ($devname, $size, $model));
2433 $disk_selector->{pve_disk_id} = $i;
2434 $disk_selector->signal_connect (changed => sub {
2435 my $w = shift;
2436 my $diskid = $w->{pve_disk_id};
2437 my $a = $w->get_active - 1;
2438 $config_options->{"disksel${diskid}"} = ($a >= 0) ? $hds->[$a] : undef;
2439 });
2440 }
2441
2442 if ($hdopion_first_setup) {
2443 $disk_selector->set_active ($i+1) if $hds->[$i];
2444 } else {
2445 my $hdind = 0;
2446 if (my $cur_hd = $config_options->{"disksel$i"}) {
2447 foreach my $hd (@$hds) {
2448 if (@$hd[1] eq @$cur_hd[1]) {
2449 $disk_selector->set_active($hdind+1);
2450 last;
2451 }
2452 $hdind++;
2453 }
2454 }
2455 }
2456
2457 push @$disk_labeled_widgets, "Harddisk $i", $disk_selector;
2458 }
2459
2460 my $scrolled_window = Gtk3::ScrolledWindow->new();
2461 $scrolled_window->set_hexpand(1);
2462 $scrolled_window->add(&$create_label_widget_grid($disk_labeled_widgets));
2463 $scrolled_window->set_policy('never', 'automatic');
2464
2465 return $scrolled_window;
2466# &$create_label_widget_grid($disk_labeled_widgets)
2467};
2468
2469my $create_raid_advanced_grid = sub {
2470 my $labeled_widgets = [];
6c99667a
FG
2471 my $spinbutton_ashift = Gtk3::SpinButton->new_with_range(9,13,1);
2472 $spinbutton_ashift->set_tooltip_text("zpool ashift property (pool sector size, default 2^12)");
2473 $spinbutton_ashift->signal_connect ("value-changed" => sub {
2474 my $w = shift;
2475 $config_options->{ashift} = $w->get_value_as_int();
c7779156
FG
2476 });
2477 $config_options->{ashift} = 12 if ! defined($config_options->{ashift});
6c99667a 2478 $spinbutton_ashift->set_value($config_options->{ashift});
c7779156 2479 push @$labeled_widgets, "ashift";
6c99667a 2480 push @$labeled_widgets, $spinbutton_ashift;
c7779156
FG
2481
2482 my $combo_compress = Gtk3::ComboBoxText->new();
2483 $combo_compress->set_tooltip_text("zfs compression algorithm for rpool dataset");
2484 # note: gzip / lze not allowed for bootfs vdevs
2485 my $comp_opts = ["on","off","lzjb","lz4"];
2486 foreach my $opt (@$comp_opts) {
2487 $combo_compress->append($opt, $opt);
2488 }
2489 $config_options->{compress} = "on" if !defined($config_options->{compress});
2490 $combo_compress->set_active_id($config_options->{compress});
2491 $combo_compress->signal_connect (changed => sub {
2492 my $w = shift;
2493 $config_options->{compress} = $w->get_active_text();
2494 });
2495 push @$labeled_widgets, "compress";
2496 push @$labeled_widgets, $combo_compress;
2497
2498 my $combo_checksum = Gtk3::ComboBoxText->new();
2499 $combo_checksum->set_tooltip_text("zfs checksum algorithm for rpool dataset");
2500 my $csum_opts = ["on", "off","fletcher2", "fletcher4", "sha256"];
2501 foreach my $opt (@$csum_opts) {
2502 $combo_checksum->append($opt, $opt);
2503 }
2504 $config_options->{checksum} = "on" if !($config_options->{checksum});
2505 $combo_checksum->set_active_id($config_options->{checksum});
2506 $combo_checksum->signal_connect (changed => sub {
2507 my $w = shift;
2508 $config_options->{checksum} = $w->get_active_text();
2509 });
2510 push @$labeled_widgets, "checksum";
2511 push @$labeled_widgets, $combo_checksum;
2512
2513 my $spinbutton_copies = Gtk3::SpinButton->new_with_range(1,3,1);
2514 $spinbutton_copies->set_tooltip_text("zfs copies property for rpool dataset (in addition to RAID redundancy!)");
2515 $spinbutton_copies->signal_connect ("value-changed" => sub {
2516 my $w = shift;
2517 $config_options->{copies} = $w->get_value_as_int();
c7779156
FG
2518 });
2519 $config_options->{copies} = 1 if !defined($config_options->{copies});
2520 $spinbutton_copies->set_value($config_options->{copies});
2521 push @$labeled_widgets, "copies", $spinbutton_copies;
2522
2523 return &$create_label_widget_grid($labeled_widgets);;
2524};
2525
aed81ff0
DM
2526sub create_hdoption_view {
2527
2528 my $dialog = Gtk3::Dialog->new();
2529
2530 $dialog->set_title("Harddisk options");
2531
2532 $dialog->add_button("_OK", 1);
2533
2534 my $contarea = $dialog->get_content_area();
2535
2536 my $hbox2 = Gtk3::Box->new('horizontal', 0);
2537 $contarea->pack_start($hbox2, 1, 1, 10);
2538
2539 my $grid = Gtk3::Grid->new();
2540 $grid->set_column_spacing(10);
2541 $grid->set_row_spacing(10);
c6ed3b24 2542
aed81ff0 2543 $hbox2->pack_start($grid, 1, 0, 10);
c6ed3b24
DM
2544
2545 my $row = 0;
2546
aed81ff0
DM
2547 # Filesystem type
2548
2549 my $label0 = Gtk3::Label->new ("Filesystem");
2550 $label0->set_alignment (1, 0.5);
c6ed3b24
DM
2551 $grid->attach($label0, 0, $row, 1, 1);
2552
bcbfab6b 2553 my $fstypecb = Gtk3::ComboBoxText->new();
aed81ff0 2554
121ebc59
DM
2555 my $fstype = ['ext3', 'ext4', 'xfs',
2556 'zfs (RAID0)', 'zfs (RAID1)',
2557 'zfs (RAID10)', 'zfs (RAIDZ-1)',
6f52fc3d
DM
2558 'zfs (RAIDZ-2)', 'zfs (RAIDZ-3)'];
2559
2560 push @$fstype, 'btrfs (RAID0)', 'btrfs (RAID1)', 'btrfs (RAID10)'
2561 if $enable_btrfs;
aed81ff0 2562
c6ed3b24
DM
2563 my $tcount = 0;
2564 foreach my $tmp (@$fstype) {
2565 $fstypecb->append_text($tmp);
2566 $fstypecb->set_active ($tcount)
2567 if $config_options->{filesys} eq $tmp;
2568 $tcount++;
2569 }
2570
2571 $grid->attach($fstypecb, 1, $row, 1, 1);
2572
2573 $hbox2->show_all();
2574
2575 $row++;
2576
c7779156
FG
2577 my $sep = Gtk3::HSeparator->new();
2578 $sep->set_visible(1);
2579 $grid->attach($sep, 0, $row, 2, 1);
2580 $row++;
aed81ff0 2581
c7779156 2582 my $hdsize_labeled_widgets = [];
aed81ff0 2583
c7779156 2584 # size compute
c6ed3b24 2585 my $hdsize = 0;
aed81ff0
DM
2586 if ( -b $target_hd) {
2587 $hdsize = int(hd_size ($target_hd) / (1024*1024.0)); # size in GB
c6ed3b24 2588 } elsif ($target_hd) {
aed81ff0
DM
2589 $hdsize = int((-s $target_hd) / (1024*1024*1024.0));
2590 }
2591
2592 my $hdsize_size_adj = Gtk3::Adjustment->new($config_options->{hdsize} || $hdsize, 0, $hdsize+1, 1, 1, 1);
2593 my $spinbutton_hdsize = Gtk3::SpinButton->new($hdsize_size_adj, 1, 1);
2594 $spinbutton_hdsize->set_tooltip_text("only use specified size (GB) of the harddisk (rest left unpartitioned)");
c7779156 2595 push @$hdsize_labeled_widgets, "hdsize", $spinbutton_hdsize;
aed81ff0
DM
2596
2597 my $entry_swapsize = Gtk3::Entry->new();
2598 $entry_swapsize->set_tooltip_text("maximum SWAP size (GB)");
2599 $entry_swapsize->signal_connect (key_press_event => \&check_float);
2600 $entry_swapsize->set_text($config_options->{swapsize}) if $config_options->{swapsize};
c7779156 2601 push @$hdsize_labeled_widgets, "swapsize", $entry_swapsize;
aed81ff0
DM
2602
2603 my $entry_maxroot = Gtk3::Entry->new();
2604 $entry_maxroot->set_tooltip_text("maximum size (GB) for LVM root volume");
2605 $entry_maxroot->signal_connect (key_press_event => \&check_float);
2606 $entry_maxroot->set_text($config_options->{maxroot}) if $config_options->{maxroot};
c7779156 2607 push @$hdsize_labeled_widgets, "maxroot", $entry_maxroot;
aed81ff0
DM
2608
2609 my $entry_minfree = Gtk3::Entry->new();
034f75e4 2610 $entry_minfree->set_tooltip_text("minimum free LVM space (GB, required for LVM snapshots)");
aed81ff0
DM
2611 $entry_minfree->signal_connect (key_press_event => \&check_float);
2612 $entry_minfree->set_text($config_options->{minfree}) if $config_options->{minfree};
c7779156 2613 push @$hdsize_labeled_widgets, "minfree", $entry_minfree;
aed81ff0
DM
2614
2615 my $entry_maxvz = Gtk3::Entry->new();
2616 $entry_maxvz->set_tooltip_text("maximum size (GB) for LVM data volume");
2617 $entry_maxvz->signal_connect (key_press_event => \&check_float);
2618 $entry_maxvz->set_text($config_options->{maxvz}) if $config_options->{maxvz};
c7779156
FG
2619 push @$hdsize_labeled_widgets, "maxvz", $entry_maxvz;
2620
2621 my $options_stack = Gtk3::Stack->new();
2622 $options_stack->set_visible(1);
2623 $options_stack->set_hexpand(1);
2624 $options_stack->set_vexpand(1);
2625 $options_stack->add_titled(&$create_raid_disk_grid(), "raiddisk", "Disk Setup");
2626 $options_stack->add_titled(&$create_label_widget_grid($hdsize_labeled_widgets), "hdsize", "Size Options");
2627 $options_stack->add_titled(&$create_raid_advanced_grid("zfs"), "raidzfsadvanced", "Advanced Options");
2628 $options_stack->set_visible_child_name("raiddisk");
2629 my $options_stack_switcher = Gtk3::StackSwitcher->new();
2630 $options_stack_switcher->set_halign('center');
2631 $options_stack_switcher->set_stack($options_stack);
2632 $grid->attach($options_stack_switcher, 0, $row, 2, 1);
2633 $row++;
2634 $grid->attach($options_stack, 0, $row, 2, 1);
c6ed3b24 2635 $row++;
aed81ff0 2636
c7779156
FG
2637 $hdopion_first_setup = 0;
2638
2639 my $switch_view = sub {
2640 my $raid = $config_options->{filesys} =~ m/zfs|btrfs/;
2641 my $enable_zfs_opts = $config_options->{filesys} =~ m/zfs/;
c6ed3b24 2642
c7779156
FG
2643 $target_hd_combo->set_visible(!$raid);
2644 $options_stack->get_child_by_name("hdsize")->set_visible(!$raid);
2645 $options_stack->get_child_by_name("raiddisk")->set_visible($raid);
2646 $options_stack_switcher->set_visible($enable_zfs_opts);
2647 $options_stack->get_child_by_name("raidzfsadvanced")->set_visible($enable_zfs_opts);
2648 if ($raid) {
c6ed3b24 2649 $target_hd_label->set_text("Target: $config_options->{filesys} ");
c7779156 2650 $options_stack->set_visible_child_name("raiddisk");
c6ed3b24 2651 } else {
c6ed3b24
DM
2652 $target_hd_label->set_text("Target Harddisk: ");
2653 }
c7779156
FG
2654 my (undef, $pref_width) = $dialog->get_preferred_width();
2655 my (undef, $pref_height) = $dialog->get_preferred_height();
2656 $dialog->resize($pref_width, $pref_height);
f7b853d1
DM
2657 };
2658
c7779156 2659 &$switch_view();
f7b853d1
DM
2660
2661 $fstypecb->signal_connect (changed => sub {
2662 $config_options->{filesys} = $fstypecb->get_active_text();
c7779156 2663 &$switch_view();
f7b853d1
DM
2664 });
2665
c6ed3b24 2666 $dialog->show();
aed81ff0
DM
2667
2668 $dialog->run();
2669
2670 my $get_float = sub {
2671 my ($entry) = @_;
2672
2673 my $text = $entry->get_text();
2674 return undef if !defined($text);
2675
2676 $text =~ s/^\s+//;
2677 $text =~ s/\s+$//;
2678
2679 return undef if $text !~ m/^\d+(\.\d+)?$/;
2680
2681 return $text;
2682 };
2683
2684 my $tmp;
2685
2686 if (($tmp = &$get_float($spinbutton_hdsize)) && ($tmp != $hdsize)) {
2687 $config_options->{hdsize} = $tmp;
2688 } else {
2689 delete $config_options->{hdsize};
2690 }
2691
2692 if (defined($tmp = &$get_float($entry_swapsize))) {
2693 $config_options->{swapsize} = $tmp;
2694 } else {
2695 delete $config_options->{swapsize};
2696 }
2697
2698 if (defined($tmp = &$get_float($entry_maxroot))) {
2699 $config_options->{maxroot} = $tmp;
2700 } else {
2701 delete $config_options->{maxroot};
2702 }
2703
2704 if (defined($tmp = &$get_float($entry_minfree))) {
2705 $config_options->{minfree} = $tmp;
2706 } else {
2707 delete $config_options->{minfree};
2708 }
2709
2710 if (defined($tmp = &$get_float($entry_maxvz))) {
2711 $config_options->{maxvz} = $tmp;
2712 } else {
2713 delete $config_options->{maxvz};
2714 }
2715
2716 $dialog->destroy();
2717}
2718
121ebc59 2719my $get_raid_devlist = sub {
c6ed3b24
DM
2720
2721 my $dev_name_hash = {};
2722
2723 my $devlist = [];
5f8e86d5 2724 for (my $i = 0; $i < @$hds; $i++) {
c6ed3b24
DM
2725 if (my $hd = $config_options->{"disksel$i"}) {
2726 my ($disk, $devname, $size, $model) = @$hd;
2727 die "device '$devname' is used more than once\n"
2728 if $dev_name_hash->{$devname};
2729 $dev_name_hash->{$devname} = $hd;
2730 push @$devlist, $hd;
2731 }
2732 }
2733
121ebc59
DM
2734 return $devlist;
2735};
2736
14aacec8
FG
2737sub zfs_mirror_size_check {
2738 my ($expected, $actual) = @_;
2739
2740 die "mirrored disks must have same size\n"
2741 if abs($expected - $actual) > $expected / 10;
2742}
2743
121ebc59
DM
2744sub get_zfs_raid_setup {
2745
2746 my $filesys = $config_options->{filesys};
2747
2748 my $devlist = &$get_raid_devlist();
2749
224bb7b0 2750 my $diskcount = scalar(@$devlist);
0cfa502c 2751 die "$filesys needs at least one device\n" if $diskcount < 1;
c6ed3b24 2752
121ebc59
DM
2753 my $bootdevlist = [];
2754
c6ed3b24
DM
2755 my $cmd= '';
2756 if ($filesys eq 'zfs (RAID0)') {
2757 push @$bootdevlist, @$devlist[0];
2758 foreach my $hd (@$devlist) {
2759 $cmd .= " @$hd[1]";
2760 }
2761 } elsif ($filesys eq 'zfs (RAID1)') {
0cfa502c 2762 die "zfs (RAID1) needs at least 2 device\n" if $diskcount < 2;
c6ed3b24 2763 $cmd .= ' mirror ';
269c66a6 2764 my $hd = @$devlist[0];
14aacec8 2765 my $expected_size = @$hd[2]; # all disks need approximately same size
269c66a6 2766 foreach $hd (@$devlist) {
14aacec8 2767 zfs_mirror_size_check($expected_size, @$hd[2]);
c6ed3b24
DM
2768 $cmd .= " @$hd[1]";
2769 push @$bootdevlist, $hd;
2770 }
2771 } elsif ($filesys eq 'zfs (RAID10)') {
0cfa502c 2772 die "zfs (RAID10) needs at least 4 device\n" if $diskcount < 4;
b8f4f0f9 2773 die "zfs (RAID10) needs an even number of devices\n" if $diskcount & 1;
c6ed3b24
DM
2774
2775 push @$bootdevlist, @$devlist[0], @$devlist[1];
2776
224bb7b0 2777 for (my $i = 0; $i < $diskcount; $i+=2) {
c6ed3b24
DM
2778 my $hd1 = @$devlist[$i];
2779 my $hd2 = @$devlist[$i+1];
14aacec8 2780 zfs_mirror_size_check(@$hd1[2], @$hd2[2]); # pairs need approximately same size
c6ed3b24
DM
2781 $cmd .= ' mirror ' . @$hd1[1] . ' ' . @$hd2[1];
2782 }
2783
2784 } elsif ($filesys =~ m/^zfs \(RAIDZ-([123])\)$/) {
2785 my $level = $1;
2786 my $mindisks = 2 + $level;
0cfa502c 2787 die "zfs (RAIDZ-$level) needs at least $mindisks devices\n" if scalar(@$devlist) < $mindisks;
269c66a6 2788 my $hd = @$devlist[0];
14aacec8 2789 my $expected_size = @$hd[2]; # all disks need approximately same size
097ecf8f 2790 $cmd .= " raidz$level";
269c66a6 2791 foreach $hd (@$devlist) {
14aacec8 2792 zfs_mirror_size_check($expected_size, @$hd[2]);
c6ed3b24
DM
2793 $cmd .= " @$hd[1]";
2794 push @$bootdevlist, $hd;
2795 }
2796 } else {
2797 die "unknown zfs mode '$filesys'\n";
2798 }
2799
2800 return ($devlist, $bootdevlist, $cmd);
2801}
2802
121ebc59
DM
2803sub get_btrfs_raid_setup {
2804
2805 my $filesys = $config_options->{filesys};
2806
2807 my $devlist = &$get_raid_devlist();
2808
2809 my $diskcount = scalar(@$devlist);
0cfa502c 2810 die "$filesys needs at least one device\n" if $diskcount < 1;
121ebc59
DM
2811
2812 my $mode;
2813
2814 if ($diskcount == 1) {
2815 $mode = 'single';
2816 } else {
2817 if ($filesys eq 'btrfs (RAID0)') {
2818 $mode = 'raid0';
2819 } elsif ($filesys eq 'btrfs (RAID1)') {
0cfa502c 2820 die "btrfs (RAID1) needs at least 2 device\n" if $diskcount < 2;
121ebc59
DM
2821 $mode = 'raid1';
2822 } elsif ($filesys eq 'btrfs (RAID10)') {
0cfa502c 2823 die "btrfs (RAID10) needs at least 4 device\n" if $diskcount < 4;
121ebc59
DM
2824 $mode = 'raid10';
2825 } else {
9d69f3d3 2826 die "unknown btrfs mode '$filesys'\n";
121ebc59
DM
2827 }
2828 }
2829
2830 return ($devlist, $mode);
2831}
2832
89a12446
DM
2833sub create_hdsel_view {
2834
2835 cleanup_view ();
2836
7becc472 2837 my $vbox = Gtk3::VBox->new (0, 0);
89a12446 2838 $inbox->pack_start ($vbox, 1, 0, 0);
7becc472 2839 my $hbox = Gtk3::HBox->new (0, 0);
53986d77 2840 $vbox->pack_start ($hbox, 0, 0, 10);
968fa90b 2841
89a12446
DM
2842 my ($disk, $devname, $size, $model) = @{@$hds[0]};
2843 $target_hd = $devname;
89a12446 2844
c6ed3b24
DM
2845 $target_hd_label = Gtk3::Label->new ("Target Harddisk: ");
2846 $hbox->pack_start ($target_hd_label, 0, 0, 0);
89a12446 2847
bcbfab6b 2848 $target_hd_combo = Gtk3::ComboBoxText->new();
89a12446 2849
1aa5bd02
DM
2850 foreach my $hd (@$hds) {
2851 ($disk, $devname, $size, $model) = @$hd;
c6ed3b24 2852 $target_hd_combo->append_text (get_device_desc ($devname, $size, $model));
1aa5bd02 2853 }
89a12446 2854
c6ed3b24
DM
2855 $target_hd_combo->set_active (0);
2856 $target_hd_combo->signal_connect (changed => sub {
1aa5bd02
DM
2857 $a = shift->get_active;
2858 my ($disk, $devname) = @{@$hds[$a]};
2859 $target_hd = $devname;
1aa5bd02 2860 });
c6ed3b24
DM
2861
2862 $hbox->pack_start ($target_hd_combo, 0, 0, 10);
aed81ff0
DM
2863
2864 my $options = Gtk3::Button->new ('_Options');
2865 $options->signal_connect (clicked => \&create_hdoption_view);
2866 $hbox->pack_start ($options, 0, 0, 0);
2867
89a12446
DM
2868
2869 $inbox->show_all;
2870
2871 display_html ("page1.htm");
c6ed3b24
DM
2872
2873 set_next (undef, sub {
2874
2875 if ($config_options->{filesys} =~ m/zfs/) {
2876 eval { get_zfs_raid_setup(); };
2877 if (my $err = $@) {
2878 display_message ("Warning: $err\n" .
269c66a6 2879 "Please fix ZFS setup first.");
c6ed3b24
DM
2880 } else {
2881 create_country_view();
2882 }
121ebc59
DM
2883 } elsif ($config_options->{filesys} =~ m/btrfs/) {
2884 eval { get_btrfs_raid_setup(); };
2885 if (my $err = $@) {
2886 display_message ("Warning: $err\n" .
2887 "Please fix BTRFS setup first.");
2888 } else {
2889 create_country_view();
2890 }
c6ed3b24
DM
2891 } else {
2892 create_country_view();
2893 }
2894 });
89a12446
DM
2895}
2896
2897sub create_extract_view {
2898
89a12446
DM
2899 cleanup_view ();
2900
2901 display_html ("extract1-license.htm");
2902 $next->set_sensitive (0);
2903
7becc472 2904 my $vbox = Gtk3::VBox->new (0, 0);
89a12446 2905 $inbox->pack_start ($vbox, 1, 0, 0);
7becc472 2906 my $hbox = Gtk3::HBox->new (0, 0);
53986d77 2907 $vbox->pack_start ($hbox, 0, 0, 10);
89a12446 2908
7becc472 2909 my $vbox2 = Gtk3::VBox->new (0, 0);
89a12446
DM
2910 $hbox->pack_start ($vbox2, 0, 0, 0);
2911
7becc472 2912 $progress_status = Gtk3::Label->new ('');
89a12446 2913 $vbox2->pack_start ($progress_status, 1, 1, 0);
968fa90b 2914
7becc472 2915 $progress = Gtk3::ProgressBar->new;
45feca6f 2916 $progress->set_show_text(1);
7becc472 2917 $progress->set_size_request (600, -1);
89a12446
DM
2918
2919 $vbox2->pack_start ($progress, 0, 0, 0);
2920
2921 $inbox->show_all;
2922
2923 my $tdir = $opt_testmode ? "target" : "/target";
2924 mkdir $tdir;
fafc616c 2925 my $base = $opt_testmode ? "/pve/$release/install/pve-base.squashfs" : "/cdrom/pve-base.squashfs";
89a12446
DM
2926
2927 eval { extract_data ($base, $tdir); };
2928 my $err = $@;
2929
2930 $next->set_sensitive (1);
2931
2932 set_next ("_Reboot", sub { exit (0); } );
2933
296cf41f
DM
2934 if ($err) {
2935 display_html ("fail.htm");
2936 display_error ($err);
2937 } else {
2938 cleanup_view ();
2939 display_html ("success.htm");
2940 }
89a12446
DM
2941}
2942
89a12446
DM
2943sub create_intro_view {
2944
2945 cleanup_view ();
2946
2947 display_html ("license.htm");
2948
2949 set_next ("I a_gree", \&create_hdsel_view);
2950}
2951
2952$ipconf = get_ip_config ();
2953
fe44bd92 2954$country = detect_country() if $ipconf->{default} || $opt_testmode;;
89a12446
DM
2955
2956# read country, kmap and timezone infos
2957$cmap = read_cmap ();
2958
2959create_main_window ();
2960
2961if (!defined ($hds) || (scalar (@$hds) <= 0)) {
2962 print "no hardisks found\n";
2963 display_html ("nohds.htm");
2964 set_next ("Reboot", sub { exit (0); } );
2965} else {
2966
2967 foreach my $hd (@$hds) {
2968 my ($disk, $devname) = @$hd;
2969 next if $devname =~ m|^/dev/md\d+$|;
2970 print "found Disk$disk N:$devname\n";
2971 }
2972
2973 create_intro_view ();
2974}
2975
7becc472 2976Gtk3->main;
89a12446
DM
2977
2978exit 0;