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