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