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