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