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