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