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