]> git.proxmox.com Git - pve-installer.git/blame - proxinstall
html: tree wide trailing whitespace cleanup
[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
1362 $vdev =~ s/ $devname/ $by_id/;
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
89a12446
DM
1604 $fstab .= "$swapfile none swap sw 0 0\n" if $swapfile;
1605
1606 $fstab .= "proc /proc proc defaults 0 0\n";
1607
71590b6a
OB
1608 write_config($fstab, "$targetdir/etc/fstab");
1609 write_config("", "$targetdir/etc/mtab");
968fa90b 1610
8ee12a1c 1611 syscmd("cp ${proxmox_libdir}/fake-start-stop-daemon $targetdir/sbin/") == 0 ||
89a12446
DM
1612 die "unable to copy start-stop-daemon\n";
1613
71590b6a
OB
1614 diversion_add($targetdir, "/sbin/start-stop-daemon", "/sbin/fake-start-stop-daemon");
1615 diversion_add($targetdir, "/usr/sbin/update-grub", "/bin/true");
1616 diversion_add($targetdir, "/usr/sbin/update-initramfs", "/bin/true");
89a12446 1617
72d2dcd0
SI
1618 my $machine_id = run_command("systemd-id128 new");
1619 die "unable to create a new machine-id\n" if ! $machine_id;
1620 write_config($machine_id, "$targetdir/etc/machine-id");
1621
a346a962
SI
1622 syscmd("cp /etc/hostid $targetdir/etc/") == 0 ||
1623 die "unable to copy hostid\n";
1624
71590b6a 1625 syscmd("touch $targetdir/proxmox_install_mode");
89a12446 1626
e35d5efb 1627 my $grub_install_devices_txt = '';
3573c046 1628 foreach my $di (@$bootdevinfo) {
e35d5efb 1629 $grub_install_devices_txt .= ', ' if $grub_install_devices_txt;
ff863262 1630 $grub_install_devices_txt .= $di->{by_id} || $di->{devname};
3573c046
DM
1631 }
1632
b1293fcb
FG
1633 # Note: keyboard-configuration/xbkb-keymap is used by console-setup
1634 my $xkmap = $cmap->{kmap}->{$keymap}->{x11} // 'us';
1464c7c9 1635
89a12446
DM
1636 debconfig_set ($targetdir, <<_EOD);
1637locales locales/default_environment_locale select en_US.UTF-8
1638locales locales/locales_to_be_generated select en_US.UTF-8 UTF-8
1639samba-common samba-common/dhcp boolean false
1640samba-common samba-common/workgroup string WORKGROUP
e953719f 1641postfix postfix/main_mailer_type select No configuration
b1293fcb 1642keyboard-configuration keyboard-configuration/xkb-keymap select $xkmap
814f5c39 1643d-i debian-installer/locale select en_US.UTF-8
3573c046 1644grub-pc grub-pc/install_devices select $grub_install_devices_txt
89a12446
DM
1645_EOD
1646
89a12446 1647 my $pkg_count = 0;
97980bf2 1648 while (<${proxmox_pkgdir}/*.deb>) { $pkg_count++ };
89a12446 1649
121ebc59
DM
1650 # btrfs/dpkg is extremely slow without --force-unsafe-io
1651 my $dpkg_opts = $use_btrfs ? "--force-unsafe-io" : "";
1652
89a12446 1653 $count = 0;
97980bf2 1654 while (<${proxmox_pkgdir}/*.deb>) {
89a12446
DM
1655 chomp;
1656 my $path = $_;
97980bf2 1657 my ($deb) = $path =~ m/${proxmox_pkgdir}\/(.*\.deb)/;
71590b6a 1658 update_progress($count/$pkg_count, 0.5, 0.75, "extracting $deb");
89a12446 1659 print "extracting: $deb\n";
71590b6a 1660 syscmd("cp $path $targetdir/tmp/$deb") == 0 ||
89a12446 1661 die "installation of package $deb failed\n";
71590b6a 1662 syscmd("chroot $targetdir dpkg $dpkg_opts --force-depends --no-triggers --unpack /tmp/$deb") == 0 ||
968fa90b 1663 die "installation of package $deb failed\n";
71590b6a 1664 update_progress((++$count)/$pkg_count, 0.5, 0.75);
89a12446
DM
1665 }
1666
3b11dce4
FG
1667 # needed for postfix postinst in case no other NIC is active
1668 syscmd("chroot $targetdir ifup lo");
1669
121ebc59 1670 my $cmd = "chroot $targetdir dpkg $dpkg_opts --force-confold --configure -a";
89a12446 1671 $count = 0;
71590b6a 1672 run_command($cmd, sub {
89a12446
DM
1673 my $line = shift;
1674 if ($line =~ m/Setting up\s+(\S+)/) {
71590b6a
OB
1675 update_progress((++$count)/$pkg_count, 0.75, 0.95,
1676 "configuring $1");
89a12446
DM
1677 }
1678 });
968fa90b 1679
89a12446
DM
1680 unlink "$targetdir/etc/mailname";
1681 $postfix_main_cf =~ s/__FQDN__/${hostname}.${domain}/;
71590b6a 1682 write_config($postfix_main_cf, "$targetdir/etc/postfix/main.cf");
89a12446
DM
1683
1684 # make sure we have all postfix directories
71590b6a 1685 syscmd("chroot $targetdir /usr/sbin/postfix check");
89a12446 1686 # cleanup mail queue
71590b6a 1687 syscmd("chroot $targetdir /usr/sbin/postsuper -d ALL");
29da2d42
SI
1688 # create /etc/aliases.db (/etc/aliases is shipped in the base squashfs)
1689 syscmd("chroot $targetdir /usr/bin/newaliases");
89a12446 1690
6b5dc3d0 1691 # enable NTP (timedatectl set-ntp true does not work without DBUS)
71590b6a 1692 syscmd("chroot $targetdir /bin/systemctl enable systemd-timesyncd.service");
6b5dc3d0 1693
89a12446
DM
1694 unlink "$targetdir/proxmox_install_mode";
1695
968fa90b 1696 # set timezone
89a12446
DM
1697 unlink ("$targetdir/etc/localtime");
1698 symlink ("/usr/share/zoneinfo/$timezone", "$targetdir/etc/localtime");
71590b6a 1699 write_config("$timezone\n", "$targetdir/etc/timezone");
89a12446 1700
89a12446
DM
1701 # set apt mirror
1702 if (my $mirror = $cmap->{country}->{$country}->{mirror}) {
1703 my $fn = "$targetdir/etc/apt/sources.list";
71590b6a 1704 syscmd("sed -i 's/ftp\\.debian\\.org/$mirror/' '$fn'");
89a12446
DM
1705 }
1706
19edf8b7
DM
1707 # create extended_states for apt (avoid cron job warning if that
1708 # file does not exist)
71590b6a 1709 write_config('', "$targetdir/var/lib/apt/extended_states");
19edf8b7 1710
c2657b8b 1711 # allow ssh root login
abcadb95 1712 syscmd(['sed', '-i', 's/^#\?PermitRootLogin.*/PermitRootLogin yes/', "$targetdir/etc/ssh/sshd_config"]);
861a26d4
DM
1713
1714 if ($setup->{product} eq 'pmg') {
1715 # install initial clamav DB
1716 my $srcdir = "${proxmox_cddir}/proxmox/clamav";
05eb99e2 1717 foreach my $fn ("main.cvd", "bytecode.cvd", "daily.cvd", "safebrowsing.cvd") {
71590b6a 1718 syscmd("cp \"$srcdir/$fn\" \"$targetdir/var/lib/clamav\"") == 0 ||
861a26d4
DM
1719 die "installation of clamav db file '$fn' failed\n";
1720 }
1721 syscmd("chroot $targetdir /bin/chown clamav:clamav -R /var/lib/clamav") == 0 ||
1722 die "unable to set owner for clamav database files\n";
1723 }
1724
58a09baa
DM
1725 if ($setup->{product} eq 'pve') {
1726 # save installer settings
1727 my $ucc = uc ($country);
1728 debconfig_set($targetdir, "pve-manager pve-manager/country string $ucc\n");
1729 }
89a12446 1730
71590b6a 1731 update_progress(0.8, 0.95, 1, "make system bootable");
89a12446 1732
5c06ced5 1733 if ($use_zfs) {
71590b6a 1734 syscmd("sed -i -e 's/^GRUB_CMDLINE_LINUX=.*/GRUB_CMDLINE_LINUX=\"root=ZFS=$zfspoolname\\/ROOT\\/$zfsrootvolname boot=zfs\"/' $targetdir/etc/default/grub") == 0 ||
5c06ced5 1735 die "unable to update /etc/default/grub\n";
4fb6ac60 1736
e38884af
SI
1737 if ($boot_type eq 'efi') {
1738 write_config("root=ZFS=$zfspoolname/ROOT/$zfsrootvolname boot=zfs", "$targetdir/etc/kernel/cmdline");
1739 }
1464c7c9 1740
5c06ced5 1741 }
23c337f5 1742
71590b6a
OB
1743 diversion_remove($targetdir, "/usr/sbin/update-grub");
1744 diversion_remove($targetdir, "/usr/sbin/update-initramfs");
89a12446 1745
56207f2a
DM
1746 my $kapi;
1747 foreach my $fn (<$targetdir/lib/modules/*>) {
1748 if ($fn =~ m!/(\d+\.\d+\.\d+-\d+-pve)$!) {
1749 die "found multiple kernels\n" if defined($kapi);
1750 $kapi = $1;
1751 }
1752 }
1753 die "unable to detect kernel version\n" if !defined($kapi);
1754
c6ed3b24 1755 if (!$opt_testmode) {
89a12446
DM
1756
1757 unlink ("$targetdir/etc/mtab");
1758 symlink ("/proc/mounts", "$targetdir/etc/mtab");
71590b6a 1759 syscmd("mount -n --bind /dev $targetdir/dev");
89a12446 1760
1f8429eb
FG
1761 my $bootloader_err_list = [];
1762 eval {
1763 syscmd("chroot $targetdir /usr/sbin/update-initramfs -c -k $kapi") == 0 ||
1764 die "unable to install initramfs\n";
1765
5ea943cf
SI
1766 my $native_4k_disk_bootable = 0;
1767 foreach my $di (@$bootdevinfo) {
1768 $native_4k_disk_bootable |= ($di->{logical_bsize} == 4096);
1769 }
1770
1f8429eb
FG
1771 foreach my $di (@$bootdevinfo) {
1772 my $dev = $di->{devname};
5ea943cf
SI
1773 if (!$native_4k_disk_bootable) {
1774 eval {
1775 syscmd("chroot $targetdir /usr/sbin/grub-install --target i386-pc --no-floppy --bootloader-id='proxmox' $dev") == 0 ||
1776 die "unable to install the i386-pc boot loader on '$dev'\n";
1777 };
1778 push @$bootloader_err_list, $@ if $@;
1779 }
1f8429eb
FG
1780
1781 eval {
1782 if (my $esp = $di->{esp}) {
1783 if ($use_zfs) {
1784 prepare_systemd_boot_esp($esp, $targetdir);
1785 } else {
1786 prepare_grub_efi_boot_esp($dev, $esp, $targetdir);
1787 }
1788 }
1789 };
1790 push @$bootloader_err_list, $@ if $@;
1e61f3d8 1791 }
89a12446 1792
1f8429eb
FG
1793 syscmd("chroot $targetdir /usr/sbin/update-grub") == 0 ||
1794 die "unable to update boot loader config\n";
1f8429eb
FG
1795 };
1796 push @$bootloader_err_list, $@ if $@;
1797
1798 if (scalar(@$bootloader_err_list) > 0) {
1799 $bootloader_err = "bootloader setup errors:\n";
1800 map { $bootloader_err .= "- $_" } @$bootloader_err_list;
1801 warn $bootloader_err;
f2afc0fc 1802 }
03c686b7 1803
71590b6a 1804 syscmd("umount $targetdir/dev");
89a12446
DM
1805 }
1806
968fa90b 1807 # cleanup
89a12446 1808
89a12446
DM
1809 unlink "$targetdir/usr/sbin/policy-rc.d";
1810
71590b6a 1811 diversion_remove($targetdir, "/sbin/start-stop-daemon");
89a12446
DM
1812
1813 # set root password
968fa90b 1814 my $octets = encode("utf-8", $password);
71590b6a
OB
1815 run_command("chroot $targetdir /usr/sbin/chpasswd", undef,
1816 "root:$octets\n");
7053f98b 1817
038552a1 1818 if ($setup->{product} eq 'pmg') {
038552a1 1819 # save admin email
71590b6a
OB
1820 write_config("section: admin\n\temail ${mailto}\n",
1821 "$targetdir/etc/pmg/pmg.conf");
038552a1
DM
1822
1823 } elsif ($setup->{product} eq 'pve') {
7053f98b 1824
8acc47b5 1825 # create pmxcfs DB
7053f98b 1826
8acc47b5
DM
1827 my $tmpdir = "$targetdir/tmp/pve";
1828 mkdir $tmpdir;
7053f98b 1829
8acc47b5
DM
1830 # write vnc keymap to datacenter.cfg
1831 my $vnckmap = $cmap->{kmap}->{$keymap}->{kvm} || 'en-us';
71590b6a
OB
1832 write_config("keyboard: $vnckmap\n",
1833 "$tmpdir/datacenter.cfg");
968fa90b 1834
8acc47b5 1835 # save admin email
71590b6a
OB
1836 write_config("user:root\@pam:1:0:::${mailto}::\n",
1837 "$tmpdir/user.cfg");
5fd81672 1838
8acc47b5 1839 # write storage.cfg
d8c697d4 1840 my $storage_cfg_fn = "$tmpdir/storage.cfg";
8acc47b5 1841 if ($use_zfs) {
71590b6a 1842 write_config($storage_cfg_zfs, $storage_cfg_fn);
8acc47b5 1843 } elsif ($use_btrfs) {
71590b6a 1844 write_config($storage_cfg_btrfs, $storage_cfg_fn);
e2c51d7c 1845 } elsif ($datadev) {
71590b6a 1846 write_config($storage_cfg_lvmthin, $storage_cfg_fn);
e2c51d7c 1847 } else {
71590b6a 1848 write_config($storage_cfg_local, $storage_cfg_fn);
8acc47b5 1849 }
7053f98b 1850
8acc47b5
DM
1851 run_command("chroot $targetdir /usr/bin/create_pmxcfs_db /tmp/pve /var/lib/pve-cluster/config.db");
1852
71590b6a 1853 syscmd("rm -rf $tmpdir");
8acc47b5 1854 }
89a12446
DM
1855 };
1856
1857 my $err = $@;
1858
71590b6a 1859 update_progress(1, 0, 1, "");
89a12446
DM
1860
1861 print $err if $err;
1862
1863 if ($opt_testmode) {
121ebc59
DM
1864 my $elapsed = Time::HiRes::tv_interval($starttime);
1865 print "Elapsed extract time: $elapsed\n";
1866
71590b6a 1867 syscmd("chroot $targetdir /usr/bin/dpkg-query -W --showformat='\${package}\n'> final.pkglist");
89a12446
DM
1868 }
1869
8d7ddbde
TL
1870 syscmd("umount $targetdir/run");
1871 syscmd("umount $targetdir/mnt/hostrun");
71590b6a
OB
1872 syscmd("umount $targetdir/tmp");
1873 syscmd("umount $targetdir/proc");
f238dd03 1874 syscmd("umount $targetdir/sys/firmware/efi/efivars");
71590b6a 1875 syscmd("umount $targetdir/sys");
6fbd1fb1
DM
1876
1877 if ($use_zfs) {
71590b6a 1878 syscmd("zfs umount -a") == 0 ||
6fbd1fb1
DM
1879 die "unable to unmount zfs\n";
1880 } else {
71590b6a 1881 syscmd("umount -d $targetdir");
6fbd1fb1 1882 }
89a12446 1883
5c06ced5 1884 if (!$err && $use_zfs) {
71590b6a 1885 syscmd("zfs set sync=standard $zfspoolname") == 0 ||
481671c3
DM
1886 die "unable to set zfs properties\n";
1887
71590b6a 1888 syscmd("zfs set mountpoint=/ $zfspoolname/ROOT/$zfsrootvolname") == 0 ||
5c06ced5 1889 die "zfs set mountpoint failed\n";
1464c7c9 1890
71590b6a 1891 syscmd("zpool set bootfs=$zfspoolname/ROOT/$zfsrootvolname $zfspoolname") == 0 ||
5c06ced5 1892 die "zfs set bootfs failed\n";
71590b6a 1893 syscmd("zpool export $zfspoolname");
5c06ced5
DM
1894 }
1895
1f8429eb
FG
1896 if ($bootloader_err) {
1897 $err = $err ? "$err\n$bootloader_err" : $bootloader_err;
1898 }
1899
89a12446
DM
1900 die $err if $err;
1901}
1902
550958aa
DM
1903my $last_display_change = 0;
1904
1905my $display_info_counter = 0;
1906
1907my $display_info_items = [
1908 "extract1-license.htm",
1909 "extract2-rulesystem.htm",
1910 "extract3-spam.htm",
1911 "extract4-virus.htm",
1912 ];
1913
1914sub display_info {
1915
1916 my $min_display_time = 15;
1917
1918 my $ctime = time();
1919
1920 return if ($ctime - $last_display_change) < $min_display_time;
1921
1922 my $page = $display_info_items->[$display_info_counter % scalar(@$display_info_items)];
1923
1924 $display_info_counter++;
1925
1926 display_html($page);
1927}
1928
89a12446
DM
1929sub display_html {
1930 my ($filename) = @_;
1931
201a5120
OB
1932 $filename = $steps[$step_number]->{html} if !$filename;
1933
a04ac176 1934 my $path = "${proxmox_libdir}/html/$filename";
c437cef5 1935
8a50920c
DM
1936 my $url = "file://$path";
1937
1938 my $data = file_get_contents($path);
1939
1940 if ($filename eq 'license.htm') {
93f25df9
TL
1941 my $license = eval { decode('utf8', file_get_contents("${proxmox_cddir}/EULA")) };
1942 if (my $err = $@) {
1943 die $err if !$opt_testmode;
1944 $license = "TESTMODE: Ignore non existent EULA...\n";
1945 }
3c866639 1946 my $title = "END USER LICENSE AGREEMENT (EULA)";
f91c161b 1947 $data =~ s/__LICENSE__/$license/;
8a50920c 1948 $data =~ s/__LICENSE_TITLE__/$title/;
3bcac16b
TL
1949 } elsif ($filename eq 'success.htm') {
1950 my $addr = $ipversion == 6 ? "[${ipaddress}]" : "$ipaddress";
1951 $data =~ s/\@IPADDR\@/$addr/;
8a50920c
DM
1952 }
1953
ed0e6aea 1954 $htmlview->load_html($data, $url);
550958aa
DM
1955
1956 $last_display_change = time();
7becc472
DM
1957}
1958
201a5120
OB
1959sub prev_function {
1960
1961 my ($text, $fctn) = @_;
1962
1963 $fctn = $step_number if !$fctn;
1964 $text = "_Previous" if !$text;
451b1da5 1965 $prev_btn->set_label ($text);
201a5120
OB
1966
1967 $step_number--;
1968 $steps[$step_number]->{function}();
1969
71590b6a 1970 $prev_btn->grab_focus();
201a5120
OB
1971}
1972
89a12446
DM
1973sub set_next {
1974 my ($text, $fctn) = @_;
1975
1976 $next_fctn = $fctn;
201a5120
OB
1977 my $step = $steps[$step_number];
1978 $text //= $steps[$step_number]->{next_button} // '_Next';
71590b6a 1979 $next->set_label($text);
968fa90b 1980
71590b6a 1981 $next->grab_focus();
89a12446 1982}
89a12446
DM
1983
1984sub create_main_window {
1985
71590b6a
OB
1986 $window = Gtk3::Window->new();
1987 $window->set_default_size(1024, 768);
84761f93 1988 $window->set_has_resize_grip(0);
71590b6a 1989 $window->set_decorated(0) if !$opt_testmode;
89a12446 1990
71590b6a 1991 my $vbox = Gtk3::VBox->new(0, 0);
89a12446 1992
782b4acd
DM
1993 my $logofn = "$setup->{product}-banner.png";
1994 my $image = Gtk3::Image->new_from_file("${proxmox_libdir}/$logofn");
71590b6a 1995 $vbox->pack_start($image, 0, 0, 0);
89a12446 1996
71590b6a
OB
1997 my $hbox = Gtk3::HBox->new(0, 0);
1998 $vbox->pack_start($hbox, 1, 1, 0);
89a12446 1999
7becc472
DM
2000 # my $f1 = Gtk3::Frame->new ('test');
2001 # $f1->set_shadow_type ('none');
2002 # $hbox->pack_start ($f1, 1, 1, 0);
89a12446 2003
71590b6a
OB
2004 my $sep1 = Gtk3::HSeparator->new();
2005 $vbox->pack_start($sep1, 0, 0, 0);
89a12446 2006
71590b6a
OB
2007 $cmdbox = Gtk3::HBox->new();
2008 $vbox->pack_start($cmdbox, 0, 0, 10);
89a12446 2009
71590b6a
OB
2010 $next = Gtk3::Button->new('_Next');
2011 $next->signal_connect(clicked => sub { $last_display_change = 0; &$next_fctn (); });
2012 $cmdbox->pack_end($next, 0, 0, 10);
201a5120
OB
2013
2014
71590b6a
OB
2015 $prev_btn = Gtk3::Button->new('_Previous');
2016 $prev_btn->signal_connect(clicked => sub { $last_display_change = 0; &prev_function (); });
2017 $cmdbox->pack_end($prev_btn, 0, 0, 10);
201a5120
OB
2018
2019
71590b6a
OB
2020 my $abort = Gtk3::Button->new('_Abort');
2021 $abort->set_can_focus(0);
2022 $cmdbox->pack_start($abort, 0, 0, 10);
2023 $abort->signal_connect(clicked => sub { exit (-1); });
89a12446 2024
71590b6a
OB
2025 my $vbox2 = Gtk3::VBox->new(0, 0);
2026 $hbox->add($vbox2);
89a12446 2027
ed0e6aea 2028 $htmlview = Gtk3::WebKit2::WebView->new();
7becc472
DM
2029 my $scrolls = Gtk3::ScrolledWindow->new();
2030 $scrolls->add($htmlview);
1464c7c9 2031
71590b6a
OB
2032 my $hbox2 = Gtk3::HBox->new(0, 0);
2033 $hbox2->pack_start($scrolls, 1, 1, 0);
89a12446 2034
71590b6a 2035 $vbox2->pack_start($hbox2, 1, 1, 0);
89a12446 2036
71590b6a
OB
2037 my $vbox3 = Gtk3::VBox->new(0, 0);
2038 $vbox2->pack_start($vbox3, 0, 0, 0);
89a12446 2039
7becc472 2040 my $sep2 = Gtk3::HSeparator->new;
71590b6a 2041 $vbox3->pack_start($sep2, 0, 0, 0);
89a12446 2042
71590b6a
OB
2043 $inbox = Gtk3::HBox->new(0, 0);
2044 $vbox3->pack_start($inbox, 0, 0, 0);
89a12446 2045
71590b6a 2046 $window->add($vbox);
89a12446
DM
2047
2048 $window->show_all;
71590b6a 2049 $window->realize();
89a12446
DM
2050}
2051
1464c7c9 2052sub cleanup_view {
d2120e51
DM
2053 $inbox->foreach(sub {
2054 my $child = shift;
1464c7c9 2055 $inbox->remove ($child);
d2120e51 2056 });
89a12446
DM
2057}
2058
aed81ff0
DM
2059# fixme: newer GTK3 has special properties to handle numbers with Entry
2060# only allow floating point numbers with Gtk3::Entry
e73c5fcf 2061
aed81ff0
DM
2062sub check_float {
2063 my ($entry, $event) = @_;
2064
e73c5fcf
FG
2065 return check_number($entry, $event, 1);
2066}
2067
2068sub check_int {
2069 my ($entry, $event) = @_;
2070
2071 return check_number($entry, $event, 0);
2072}
2073
2074sub check_number {
2075 my ($entry, $event, $float) = @_;
aed81ff0
DM
2076
2077 my $val = $event->get_keyval;
2078
e73c5fcf 2079 if (($float && $val == ord '.') ||
aed81ff0
DM
2080 $val == Gtk3::Gdk::KEY_ISO_Left_Tab ||
2081 $val == Gtk3::Gdk::KEY_Shift_L ||
2082 $val == Gtk3::Gdk::KEY_Tab ||
2083 $val == Gtk3::Gdk::KEY_Left ||
2084 $val == Gtk3::Gdk::KEY_Right ||
2085 $val == Gtk3::Gdk::KEY_BackSpace ||
2086 $val == Gtk3::Gdk::KEY_Delete ||
2087 ($val >= ord '0' && $val <= ord '9') ||
2088 ($val >= Gtk3::Gdk::KEY_KP_0 &&
2089 $val <= Gtk3::Gdk::KEY_KP_9)) {
2090 return undef;
2091 }
2092
2093 return 1;
2094}
2095
d2120e51 2096sub create_text_input {
89a12446
DM
2097 my ($default, $text) = @_;
2098
71590b6a 2099 my $hbox = Gtk3::HBox->new(0, 0);
89a12446 2100
71590b6a
OB
2101 my $label = Gtk3::Label->new($text);
2102 $label->set_size_request(150, -1);
2103 $label->set_alignment(1, 0.5);
2104 $hbox->pack_start($label, 0, 0, 10);
2105 my $e1 = Gtk3::Entry->new();
2106 $e1->set_width_chars(30);
2107 $hbox->pack_start($e1, 0, 0, 0);
2108 $e1->set_text($default);
89a12446
DM
2109
2110 return ($hbox, $e1);
2111}
2112
89a12446
DM
2113sub get_ip_config {
2114
fe44bd92
FG
2115 my $ifaces = {};
2116 my $default;
89a12446 2117
fe44bd92
FG
2118 my $links = `ip -o l`;
2119 foreach my $l (split /\n/,$links) {
2120 my ($index, $name, $flags, $state, $mac) = $l =~ m/^(\d+):\s+(\S+):\s+<(\S+)>.*\s+state\s+(\S+)\s+.*\s+link\/ether\s+(\S+)\s+/;
2121 next if !$name || $name eq 'lo';
89a12446 2122
fe44bd92
FG
2123 my $driver = readlink "/sys/class/net/$name/device/driver" || 'unknown';
2124 $driver =~ s!^.*/!!;
2125
2126 $ifaces->{"$index"} = {
2127 name => $name,
2128 driver => $driver,
2129 flags => $flags,
2130 state => $state,
2131 mac => $mac,
2132 };
2133
2134 my $addresses = `ip -o a s $name`;
2135 foreach my $a (split /\n/,$addresses) {
2136 my ($family, $ip, $prefix) = $a =~ m/^\Q$index\E:\s+\Q$name\E\s+(inet|inet6)\s+($IPRE)\/(\d+)\s+/;
2137 next if !$ip;
32b6fbcf 2138 next if $a =~ /scope\s+link/; # ignore link local
fe44bd92
FG
2139
2140 my $mask = $prefix;
2141
2142 if ($family eq 'inet') {
2143 next if !$ip =~ /$IPV4RE/;
2144 next if $prefix < 8 || $prefix > 32;
2145 $mask = @$ipv4_reverse_mask[$prefix];
2146 } else {
2147 next if !$ip =~ /$IPV6RE/;
2148 }
2149
2150 $default = $index if !$default;
2151
2152 $ifaces->{"$index"}->{"$family"} = {
2153 mask => $mask,
2154 addr => $ip,
2155 };
2156 }
2157 }
2158
2159
2160 my $route = `ip route`;
2161 my ($gateway) = $route =~ m/^default\s+via\s+(\S+)\s+/m;
89a12446
DM
2162
2163 my $resolvconf = `cat /etc/resolv.conf`;
2164 my ($dnsserver) = $resolvconf =~ m/^nameserver\s+(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})$/m;
713790a4 2165 my ($domain) = $resolvconf =~ m/^domain\s+(\S+)$/m;
89a12446
DM
2166
2167 return {
fe44bd92
FG
2168 default => $default,
2169 ifaces => $ifaces,
89a12446
DM
2170 gateway => $gateway,
2171 dnsserver => $dnsserver,
713790a4 2172 domain => $domain,
89a12446
DM
2173 }
2174}
2175
2176sub display_message {
2177 my ($msg) = @_;
2178
71590b6a
OB
2179 my $dialog = Gtk3::MessageDialog->new($window, 'modal',
2180 'info', 'ok', $msg);
89a12446
DM
2181 $dialog->run();
2182 $dialog->destroy();
2183}
2184
2185sub display_error {
2186 my ($msg) = @_;
2187
71590b6a
OB
2188 my $dialog = Gtk3::MessageDialog->new($window, 'modal',
2189 'error', 'ok', $msg);
89a12446
DM
2190 $dialog->run();
2191 $dialog->destroy();
2192}
2193
fe44bd92
FG
2194my $ipconf_first_view = 1;
2195
89a12446
DM
2196sub create_ipconf_view {
2197
201a5120
OB
2198 cleanup_view();
2199 display_html();
89a12446 2200
71590b6a
OB
2201 my $vbox = Gtk3::VBox->new(0, 0);
2202 $inbox->pack_start($vbox, 1, 0, 0);
2203 my $hbox = Gtk3::HBox->new(0, 0);
2204 $vbox->pack_start($hbox, 0, 0, 10);
2205 my $vbox2 = Gtk3::VBox->new(0, 0);
2206 $hbox->add($vbox2);
89a12446 2207
ebc4f76f 2208 my $ipaddr_text = $config->{ipaddress} // "192.168.100.2";
fe44bd92
FG
2209 my $ipbox;
2210 ($ipbox, $ipconf_entry_addr) =
71590b6a 2211 create_text_input($ipaddr_text, 'IP Address:');
fe44bd92 2212
ebc4f76f 2213 my $netmask_text = $config->{netmask} // "255.255.255.0";
fe44bd92
FG
2214 my $maskbox;
2215 ($maskbox, $ipconf_entry_mask) =
71590b6a 2216 create_text_input($netmask_text, 'Netmask:');
fe44bd92
FG
2217
2218 my $device_cb = Gtk3::ComboBoxText->new();
2219 $device_cb->set_active(0);
2220 $device_cb->set_visible(1);
2221
2222 my $get_device_desc = sub {
2223 my $iface = shift;
2224 return "$iface->{name} - $iface->{mac} ($iface->{driver})";
2225 };
2226
2227 my $device_active_map = {};
ebc4f76f 2228 my $device_active_reverse_map = {};
5b6ba737
FG
2229
2230 my $device_change_handler = sub {
2231 my $current = shift;
d6524c52
TL
2232
2233 my $new = $device_active_map->{$current->get_active()};
cd2d2a27 2234 return if defined($ipconf->{selected}) && $new eq $ipconf->{selected};
d6524c52
TL
2235
2236 $ipconf->{selected} = $new;
5b6ba737 2237 my $iface = $ipconf->{ifaces}->{$ipconf->{selected}};
ebc4f76f 2238 $config->{mngmt_nic} = $iface->{name};
5b6ba737
FG
2239 $ipconf_entry_addr->set_text($iface->{inet}->{addr} || $iface->{inet6}->{addr})
2240 if $iface->{inet}->{addr} || $iface->{inet6}->{addr};
2241 $ipconf_entry_mask->set_text($iface->{inet}->{mask} || $iface->{inet6}->{mask})
2242 if $iface->{inet}->{mask} || $iface->{inet6}->{mask};
2243 };
2244
fe44bd92
FG
2245 my $i = 0;
2246 foreach my $index (sort keys %{$ipconf->{ifaces}}) {
2247 $device_cb->append_text(&$get_device_desc($ipconf->{ifaces}->{$index}));
ebc4f76f
TL
2248 $device_active_map->{$i} = $index;
2249 $device_active_reverse_map->{$ipconf->{ifaces}->{$index}->{name}} = $i;
fe44bd92
FG
2250 if ($ipconf_first_view && $index == $ipconf->{default}) {
2251 $device_cb->set_active($i);
5b6ba737 2252 &$device_change_handler($device_cb);
fe44bd92
FG
2253 $ipconf_first_view = 0;
2254 }
71590b6a 2255 $device_cb->signal_connect('changed' => $device_change_handler);
fe44bd92
FG
2256 $i++;
2257 }
2258
ebc4f76f
TL
2259 if (my $nic = $config->{mngmt_nic}) {
2260 $device_cb->set_active($device_active_reverse_map->{$nic} // 0);
2261 } else {
2262 $device_cb->set_active(0);
2263 }
5b6ba737 2264
71590b6a
OB
2265 my $devicebox = Gtk3::HBox->new(0, 0);
2266 my $label = Gtk3::Label->new("Management Interface:");
2267 $label->set_size_request(150, -1);
2268 $label->set_alignment(1, 0.5);
2269 $devicebox->pack_start($label, 0, 0, 10);
2270 $devicebox->pack_start($device_cb, 0, 0, 0);
fe44bd92 2271
71590b6a 2272 $vbox2->pack_start($devicebox, 0, 0, 2);
968fa90b 2273
ebc4f76f 2274 my $hn = $config->{fqdn} // "$setup->{product}." . ($ipconf->{domain} // "example.invalid");
1464c7c9 2275
968fa90b 2276 my ($hostbox, $hostentry) =
71590b6a
OB
2277 create_text_input($hn, 'Hostname (FQDN):');
2278 $vbox2->pack_start($hostbox, 0, 0, 2);
89a12446 2279
71590b6a 2280 $vbox2->pack_start($ipbox, 0, 0, 2);
89a12446 2281
71590b6a 2282 $vbox2->pack_start($maskbox, 0, 0, 2);
89a12446 2283
ebc4f76f 2284 $gateway = $config->{gateway} // $ipconf->{gateway} || '192.168.100.1';
89a12446
DM
2285
2286 my $gwbox;
d2120e51 2287 ($gwbox, $ipconf_entry_gw) =
71590b6a 2288 create_text_input($gateway, 'Gateway:');
89a12446 2289
71590b6a 2290 $vbox2->pack_start($gwbox, 0, 0, 2);
89a12446 2291
ebc4f76f 2292 $dnsserver = $config->{dnsserver} // $ipconf->{dnsserver} || $gateway;
89a12446
DM
2293
2294 my $dnsbox;
d2120e51 2295 ($dnsbox, $ipconf_entry_dns) =
71590b6a 2296 create_text_input($dnsserver, 'DNS Server:');
89a12446 2297
71590b6a 2298 $vbox2->pack_start($dnsbox, 0, 0, 0);
89a12446
DM
2299
2300 $inbox->show_all;
71590b6a 2301 set_next(undef, sub {
d2120e51
DM
2302
2303 # verify hostname
1464c7c9 2304
89a12446 2305 my $text = $hostentry->get_text();
968fa90b 2306
89a12446
DM
2307 $text =~ s/^\s+//;
2308 $text =~ s/\s+$//;
2309
ebc4f76f
TL
2310 $config->{fqdn} = $text;
2311
ac3757a9 2312 my $namere = "([a-zA-Z0-9]([a-zA-Z0-9\-]*[a-zA-Z0-9])?)";
968fa90b 2313
24973868
WB
2314 # Debian does not support purely numeric hostnames
2315 if ($text && $text =~ /^[0-9]+(?:\.|$)/) {
2316 display_message("Purely numeric hostnames are not allowed.");
2317 $hostentry->grab_focus();
2318 return;
2319 }
2320
a39bc1f2 2321 if ($text && $text =~ m/^(${namere}\.)*${namere}$/ && $text !~ m/.example.invalid$/ &&
89a12446
DM
2322 $text =~ m/^([^\.]+)\.(\S+)$/) {
2323 $hostname = $1;
2324 $domain = $2;
d2120e51 2325 } else {
71590b6a 2326 display_message("Hostname does not look like a fully qualified domain name.");
d2120e51 2327 $hostentry->grab_focus();
89a12446
DM
2328 return;
2329 }
d2120e51
DM
2330
2331 # verify ip address
2332
2333 $text = $ipconf_entry_addr->get_text();
2334 $text =~ s/^\s+//;
2335 $text =~ s/\s+$//;
2336 if ($text =~ m!^($IPV4RE)$!) {
2337 $ipaddress = $text;
b6200603
DM
2338 $ipversion = 4;
2339 } elsif ($text =~ m!^($IPV6RE)$!) {
1464c7c9 2340 $ipaddress = $text;
b6200603 2341 $ipversion = 6;
d2120e51 2342 } else {
71590b6a 2343 display_message("IP address is not valid.");
d2120e51
DM
2344 $ipconf_entry_addr->grab_focus();
2345 return;
2346 }
ebc4f76f 2347 $config->{ipaddress} = $ipaddress;
d2120e51
DM
2348
2349 $text = $ipconf_entry_mask->get_text();
2350 $text =~ s/^\s+//;
2351 $text =~ s/\s+$//;
b6200603
DM
2352 if (($ipversion == 6) && ($text =~ m/^(\d+)$/) && ($1 >= 8) && ($1 <= 126)) {
2353 $netmask = $text;
2354 } elsif (($ipversion == 4) && defined($ipv4_mask_hash->{$text})) {
d2120e51
DM
2355 $netmask = $text;
2356 } else {
71590b6a 2357 display_message("Netmask is not valid.");
d2120e51
DM
2358 $ipconf_entry_mask->grab_focus();
2359 return;
2360 }
ebc4f76f 2361 $config->{netmask} = $netmask;
d2120e51
DM
2362
2363 $text = $ipconf_entry_gw->get_text();
2364 $text =~ s/^\s+//;
2365 $text =~ s/\s+$//;
b6200603
DM
2366 if (($ipversion == 4) && ($text =~ m!^($IPV4RE)$!)) {
2367 $gateway = $text;
2368 } elsif (($ipversion == 6) && ($text =~ m!^($IPV6RE)$!)) {
d2120e51
DM
2369 $gateway = $text;
2370 } else {
71590b6a 2371 display_message("Gateway is not valid.");
d2120e51
DM
2372 $ipconf_entry_gw->grab_focus();
2373 return;
2374 }
ebc4f76f 2375 $config->{gateway} = $gateway;
1464c7c9 2376
d2120e51
DM
2377 $text = $ipconf_entry_dns->get_text();
2378 $text =~ s/^\s+//;
2379 $text =~ s/\s+$//;
b6200603
DM
2380 if (($ipversion == 4) && ($text =~ m!^($IPV4RE)$!)) {
2381 $dnsserver = $text;
2382 } elsif (($ipversion == 6) && ($text =~ m!^($IPV6RE)$!)) {
d2120e51
DM
2383 $dnsserver = $text;
2384 } else {
71590b6a 2385 display_message("DNS server is not valid.");
d2120e51
DM
2386 $ipconf_entry_dns->grab_focus();
2387 return;
2388 }
ebc4f76f 2389 $config->{dnsserver} = $dnsserver;
1464c7c9 2390
d2120e51 2391 #print "TEST $ipaddress $netmask $gateway $dnsserver\n";
1464c7c9 2392
201a5120 2393 $step_number++;
2e33c3f0 2394 create_ack_view();
89a12446
DM
2395 });
2396
2397 $hostentry->grab_focus();
2398}
2399
2e33c3f0
OB
2400sub create_ack_view {
2401
2402 cleanup_view();
2403
2404 my $ack_template = "${proxmox_libdir}/html/ack_template.htm";
2405 my $ack_html = "${proxmox_libdir}/html/$steps[$step_number]->{html}";
2406 my $html_data = file_get_contents($ack_template);
2407
2408 my %config_values = (
a7d40341 2409 __target_hd__ => join(' | ', @{$config_options->{target_hds}}),
0470018e 2410 __target_fs__ => $config_options->{filesys},
0ddd2227 2411 __country__ => $cmap->{country}->{$country}->{name},
2e33c3f0
OB
2412 __timezone__ => $timezone,
2413 __keymap__ => $keymap,
2414 __mailto__ => $mailto,
2415 __interface__ => $ipconf->{ifaces}->{$ipconf->{selected}}->{name},
2416 __hostname__ => $hostname,
2417 __ip__ => $ipaddress,
2418 __netmask__ => $netmask,
2419 __gateway__ => $gateway,
2420 __dnsserver__ => $dnsserver,
2421 );
2422
2423 while ( my ($k, $v) = each %config_values) {
2424 $html_data =~ s/$k/$v/g;
2425 }
2426
2427 write_config($html_data, $ack_html);
2428
2429 display_html();
2430
2431 set_next(undef, sub {
2432 $step_number++;
2433 create_extract_view();
2434 });
2435}
2436
89a12446
DM
2437sub get_device_desc {
2438 my ($devname, $size, $model) = @_;
2439
d2120e51 2440 if ($size && ($size > 0)) {
1bd457bb 2441 $size = int($size/2048); # size in MB, from 512B "sectors"
89a12446 2442
d2120e51 2443 my $text = "$devname (";
89a12446
DM
2444 if ($size >= 1024) {
2445 $size = int($size/1024); # size in GB
d2120e51 2446 $text .= "${size}GB";
89a12446 2447 } else {
d2120e51 2448 $text .= "${size}MB";
89a12446
DM
2449 }
2450
d2120e51
DM
2451 $text .= ", $model" if $model;
2452 $text .= ")";
2453
89a12446
DM
2454 } else {
2455 return $devname;
2456 }
2457}
2458
2459sub update_layout {
2460 my ($cb, $kmap) = @_;
2461
2462 my $ind;
2463 my $def;
2464 my $i = 0;
2465 my $kmaphash = $cmap->{kmaphash};
2466 foreach my $layout (sort keys %$kmaphash) {
2467 $def = $i if $kmaphash->{$layout} eq 'en-us';
2468 $ind = $i if $kmap && $kmaphash->{$layout} eq $kmap;
2469 $i++;
2470 }
2471
71590b6a 2472 $cb->set_active($ind || $def || 0);
89a12446
DM
2473}
2474
2475my $lastzonecb;
2476sub update_zonelist {
2477 my ($box, $cc) = @_;
2478
2479 my $cczones = $cmap->{cczones};
2480 my $zones = $cmap->{zones};
2481
2482 my $sel;
2483 if ($lastzonecb) {
2484 $sel = $lastzonecb->get_active_text();
2485 $box->remove ($lastzonecb);
2486 } else {
2487 $sel = $timezone; # used once to select default
2488 }
2489
bcbfab6b 2490 my $cb = $lastzonecb = Gtk3::ComboBoxText->new();
71590b6a 2491 $cb->set_size_request(200, -1);
89a12446 2492
71590b6a 2493 $cb->signal_connect('changed' => sub {
89a12446
DM
2494 $timezone = $cb->get_active_text();
2495 });
2496
2497 my @za;
2498 if ($cc && defined ($cczones->{$cc})) {
2499 @za = keys %{$cczones->{$cc}};
2500 } else {
2501 @za = keys %$zones;
2502 }
2503 my $ind;
2504 my $i = 0;
2505 foreach my $zone (sort @za) {
2506 $ind = $i if $sel && $zone eq $sel;
71590b6a 2507 $cb->append_text($zone);
89a12446
DM
2508 $i++;
2509 }
2510
71590b6a 2511 $cb->set_active($ind || 0);
89a12446
DM
2512
2513 $cb->show;
71590b6a 2514 $box->pack_start($cb, 0, 0, 0);
89a12446
DM
2515}
2516
2517sub create_password_view {
2518
71590b6a 2519 cleanup_view();
89a12446 2520
71590b6a
OB
2521 my $vbox2 = Gtk3::VBox->new(0, 0);
2522 $inbox->pack_start($vbox2, 1, 0, 0);
2523 my $vbox = Gtk3::VBox->new(0, 0);
2524 $vbox2->pack_start($vbox, 0, 0, 10);
2525
2526 my $hbox1 = Gtk3::HBox->new(0, 0);
2527 my $label = Gtk3::Label->new("Password");
2528 $label->set_size_request(150, -1);
2529 $label->set_alignment(1, 0.5);
2530 $hbox1->pack_start($label, 0, 0, 10);
2531 my $pwe1 = Gtk3::Entry->new();
2532 $pwe1->set_visibility(0);
201a5120 2533 $pwe1->set_text($password) if $password;
71590b6a
OB
2534 $pwe1->set_size_request(200, -1);
2535 $hbox1->pack_start($pwe1, 0, 0, 0);
2536
2537 my $hbox2 = Gtk3::HBox->new(0, 0);
2538 $label = Gtk3::Label->new("Confirm");
2539 $label->set_size_request(150, -1);
2540 $label->set_alignment(1, 0.5);
2541 $hbox2->pack_start($label, 0, 0, 10);
2542 my $pwe2 = Gtk3::Entry->new();
2543 $pwe2->set_visibility(0);
201a5120 2544 $pwe2->set_text($password) if $password;
71590b6a
OB
2545 $pwe2->set_size_request(200, -1);
2546 $hbox2->pack_start($pwe2, 0, 0, 0);
2547
2548 my $hbox3 = Gtk3::HBox->new(0, 0);
2549 $label = Gtk3::Label->new("E-Mail");
2550 $label->set_size_request(150, -1);
2551 $label->set_alignment(1, 0.5);
2552 $hbox3->pack_start($label, 0, 0, 10);
2553 my $eme = Gtk3::Entry->new();
2554 $eme->set_size_request(200, -1);
201a5120 2555 $eme->set_text($mailto);
71590b6a 2556 $hbox3->pack_start($eme, 0, 0, 0);
89a12446
DM
2557
2558
71590b6a
OB
2559 $vbox->pack_start($hbox1, 0, 0, 5);
2560 $vbox->pack_start($hbox2, 0, 0, 5);
2561 $vbox->pack_start($hbox3, 0, 0, 15);
89a12446
DM
2562
2563 $inbox->show_all;
2564
201a5120 2565 display_html();
89a12446
DM
2566
2567 set_next (undef, sub {
2568
2569 my $t1 = $pwe1->get_text;
2570 my $t2 = $pwe2->get_text;
2571
2572 if (length ($t1) < 5) {
71590b6a 2573 display_message("Password is too short.");
89a12446
DM
2574 $pwe1->grab_focus();
2575 return;
2576 }
2577
2578 if ($t1 ne $t2) {
71590b6a 2579 display_message("Password does not match.");
89a12446
DM
2580 $pwe1->grab_focus();
2581 return;
2582 }
2583
2584 my $t3 = $eme->get_text;
c82fffd8 2585 if ($t3 !~ m/^[\w\+\-\~]+(\.[\w\+\-\~]+)*@[a-zA-Z0-9\-]+(\.[a-zA-Z0-9\-]+)*$/) {
71590b6a 2586 display_message("E-Mail does not look like a valid address" .
89a12446
DM
2587 " (user\@domain.tld)");
2588 $eme->grab_focus();
2589 return;
a39bc1f2 2590 }
89a12446 2591
a39bc1f2 2592 if ($t3 eq 'mail@example.invalid') {
71590b6a 2593 display_message("Please enter a valid E-Mail address");
a39bc1f2
FG
2594 $eme->grab_focus();
2595 return;
89a12446
DM
2596 }
2597
2598 $password = $t1;
2599 $mailto = $t3;
2600
201a5120 2601 $step_number++;
89a12446
DM
2602 create_ipconf_view();
2603 });
2604
2605 $pwe1->grab_focus();
2606
2607}
2608
2609sub create_country_view {
2610
71590b6a 2611 cleanup_view();
89a12446
DM
2612
2613 my $countryhash = $cmap->{countryhash};
2614 my $ctr = $cmap->{country};
2615
71590b6a
OB
2616 my $vbox2 = Gtk3::VBox->new(0, 0);
2617 $inbox->pack_start($vbox2, 1, 0, 0);
2618 my $vbox = Gtk3::VBox->new(0, 0);
2619 $vbox2->pack_start($vbox, 0, 0, 10);
89a12446 2620
71590b6a
OB
2621 my $w = Gtk3::Entry->new();
2622 $w->set_size_request(200, -1);
89a12446 2623
71590b6a
OB
2624 my $c = Gtk3::EntryCompletion->new();
2625 $c->set_text_column(0);
89a12446 2626 $c->set_minimum_key_length(0);
71590b6a
OB
2627 $c->set_popup_set_width(1);
2628 $c->set_inline_completion(1);
2629
2630 my $hbox2 = Gtk3::HBox->new(0, 0);
2631 my $label = Gtk3::Label->new("Time zone");
2632 $label->set_size_request(150, -1);
2633 $label->set_alignment(1, 0.5);
2634 $hbox2->pack_start($label, 0, 0, 10);
89a12446
DM
2635 update_zonelist ($hbox2);
2636
71590b6a
OB
2637 my $hbox3 = Gtk3::HBox->new(0, 0);
2638 $label = Gtk3::Label->new("Keyboard Layout");
2639 $label->set_size_request(150, -1);
2640 $label->set_alignment(1, 0.5);
2641 $hbox3->pack_start($label, 0, 0, 10);
89a12446 2642
bcbfab6b 2643 my $kmapcb = Gtk3::ComboBoxText->new();
89a12446
DM
2644 $kmapcb->set_size_request (200, -1);
2645 foreach my $layout (sort keys %{$cmap->{kmaphash}}) {
2646 $kmapcb->append_text ($layout);
2647 }
2648
71590b6a 2649 update_layout($kmapcb);
89a12446
DM
2650 $hbox3->pack_start ($kmapcb, 0, 0, 0);
2651
2652 $kmapcb->signal_connect ('changed' => sub {
2653 my $sel = $kmapcb->get_active_text();
2654 if (my $kmap = $cmap->{kmaphash}->{$sel}) {
2655 my $xkmap = $cmap->{kmap}->{$kmap}->{x11};
2656 my $xvar = $cmap->{kmap}->{$kmap}->{x11var};
2657 syscmd ("setxkbmap $xkmap $xvar") if !$opt_testmode;
2658 $keymap = $kmap;
2659 }
2660 });
2661
2662 $w->signal_connect ('changed' => sub {
2663 my ($entry, $event) = @_;
2664 my $text = $entry->get_text;
2665
2666 if (my $cc = $countryhash->{lc($text)}) {
71590b6a 2667 update_zonelist($hbox2, $cc);
89a12446 2668 my $kmap = $ctr->{$cc}->{kmap} || 'en-us';
71590b6a 2669 update_layout($kmapcb, $kmap);
89a12446
DM
2670 }
2671 });
2672
2673 $w->signal_connect (key_press_event => sub {
2674 my ($entry, $event) = @_;
2675 my $text = $entry->get_text;
2676
7becc472
DM
2677 my $val = $event->get_keyval;
2678
2679 if ($val == Gtk3::Gdk::KEY_Tab) {
89a12446 2680 my $cc = $countryhash->{lc($text)};
1464c7c9 2681
89a12446
DM
2682 my $found = 0;
2683 my $compl;
7becc472 2684
4443aa27
DM
2685 if ($cc) {
2686 $found = 1;
2687 $compl = $ctr->{$cc}->{name};
2688 } else {
2689 foreach my $cc (keys %$ctr) {
2690 my $ct = $ctr->{$cc}->{name};
2691 if ($ct =~ m/^\Q$text\E.*$/i) {
2692 $found++;
2693 $compl = $ct;
2694 }
2695 last if $found > 1;
89a12446 2696 }
89a12446 2697 }
4443aa27 2698
89a12446 2699 if ($found == 1) {
7becc472 2700 $entry->set_text($compl);
3df718ea 2701 $c->complete();
89a12446
DM
2702 return undef;
2703 } else {
7becc472
DM
2704 #Gtk3::Gdk::beep();
2705 print chr(7); # beep ?
89a12446
DM
2706 }
2707
3df718ea
DM
2708 $c->complete();
2709
7becc472
DM
2710 my $buf = $w->get_buffer();
2711 $buf->insert_text(-1, '', -1); # popup selection
2712
89a12446
DM
2713 return 1;
2714 }
2715
2716 return undef;
2717 });
1464c7c9 2718
7becc472 2719 my $ls = Gtk3::ListStore->new('Glib::String');
89a12446
DM
2720 foreach my $cc (sort {$ctr->{$a}->{name} cmp $ctr->{$b}->{name} } keys %$ctr) {
2721 my $iter = $ls->append();
2722 $ls->set ($iter, 0, $ctr->{$cc}->{name});
2723 }
2724 $c->set_model ($ls);
2725
968fa90b 2726 $w->set_completion ($c);
89a12446 2727
71590b6a 2728 my $hbox = Gtk3::HBox->new(0, 0);
89a12446 2729
71590b6a
OB
2730 $label = Gtk3::Label->new("Country");
2731 $label->set_alignment(1, 0.5);
2732 $label->set_size_request(150, -1);
2733 $hbox->pack_start($label, 0, 0, 10);
2734 $hbox->pack_start($w, 0, 0, 0);
89a12446 2735
71590b6a
OB
2736 $vbox->pack_start($hbox, 0, 0, 5);
2737 $vbox->pack_start($hbox2, 0, 0, 5);
2738 $vbox->pack_start($hbox3, 0, 0, 5);
89a12446 2739
9d1f1ee3 2740 if ($country && $ctr->{$country}) {
89a12446
DM
2741 $w->set_text ($ctr->{$country}->{name});
2742 }
2743
2744 $inbox->show_all;
2745
201a5120 2746 display_html();
89a12446
DM
2747 set_next (undef, sub {
2748
2749 my $text = $w->get_text;
2750
2751 if (my $cc = $countryhash->{lc($text)}) {
2752 $country = $cc;
201a5120 2753 $step_number++;
89a12446
DM
2754 create_password_view();
2755 return;
2756 } else {
71590b6a 2757 display_message("Please select a country first.");
89a12446
DM
2758 $w->grab_focus();
2759 }
2760 });
2761
2762 $w->grab_focus();
2763}
2764
c6ed3b24
DM
2765my $target_hd_combo;
2766my $target_hd_label;
2767
bd3a2e26 2768my $hdoption_first_setup = 1;
c6ed3b24 2769
c7779156
FG
2770my $create_basic_grid = sub {
2771 my $grid = Gtk3::Grid->new();
2772 $grid->set_visible(1);
2773 $grid->set_column_spacing(10);
2774 $grid->set_row_spacing(10);
2775 $grid->set_hexpand(1);
2776
2777 $grid->set_margin_start(5);
2778 $grid->set_margin_end(5);
2779 $grid->set_margin_top(5);
2780 $grid->set_margin_bottom(5);
2781
2782 return $grid;
2783};
2784
2785my $create_label_widget_grid = sub {
2786 my ($labeled_widgets) = @_;
2787
2788 my $grid = &$create_basic_grid();
2789 my $row = 0;
2790
2791 for (my $i = 0; $i < @$labeled_widgets; $i += 2) {
2792 my $widget = @$labeled_widgets[$i+1];
2793 my $label = Gtk3::Label->new(@$labeled_widgets[$i]);
2794 $label->set_visible(1);
2795 $label->set_alignment (1, 0.5);
2796 $grid->attach($label, 0, $row, 1, 1);
2797 $widget->set_visible(1);
2798 $grid->attach($widget, 1, $row, 1, 1);
2799 $row++;
2800 }
2801
2802 return $grid;
2803};
2804
2805my $create_raid_disk_grid = sub {
2806 my $disk_labeled_widgets = [];
2807 for (my $i = 0; $i < @$hds; $i++) {
2808 my $disk_selector = Gtk3::ComboBoxText->new();
2809 $disk_selector->append_text("-- do not use --");
2810 $disk_selector->set_active(0);
2811 $disk_selector->set_visible(1);
2812 foreach my $hd (@$hds) {
17fd908e 2813 my ($disk, $devname, $size, $model, $logical_bsize) = @$hd;
c7779156
FG
2814 $disk_selector->append_text(get_device_desc ($devname, $size, $model));
2815 $disk_selector->{pve_disk_id} = $i;
2816 $disk_selector->signal_connect (changed => sub {
2817 my $w = shift;
2818 my $diskid = $w->{pve_disk_id};
2819 my $a = $w->get_active - 1;
2820 $config_options->{"disksel${diskid}"} = ($a >= 0) ? $hds->[$a] : undef;
2821 });
2822 }
2823
bd3a2e26 2824 if ($hdoption_first_setup) {
c7779156
FG
2825 $disk_selector->set_active ($i+1) if $hds->[$i];
2826 } else {
2827 my $hdind = 0;
2828 if (my $cur_hd = $config_options->{"disksel$i"}) {
2829 foreach my $hd (@$hds) {
2830 if (@$hd[1] eq @$cur_hd[1]) {
2831 $disk_selector->set_active($hdind+1);
2832 last;
2833 }
2834 $hdind++;
2835 }
2836 }
2837 }
2838
2839 push @$disk_labeled_widgets, "Harddisk $i", $disk_selector;
2840 }
2841
2842 my $scrolled_window = Gtk3::ScrolledWindow->new();
2843 $scrolled_window->set_hexpand(1);
650a9aab 2844 $scrolled_window->set_propagate_natural_height(1) if @$hds > 4;
c7779156
FG
2845 $scrolled_window->add(&$create_label_widget_grid($disk_labeled_widgets));
2846 $scrolled_window->set_policy('never', 'automatic');
2847
2848 return $scrolled_window;
2849# &$create_label_widget_grid($disk_labeled_widgets)
2850};
2851
d7fe65ff
TL
2852# shared between different ui parts (e.g., ZFS and "normal" single disk FS)
2853my $hdsize_size_adj;
2854my $hdsize_entry_buffer;
2855
2856my $get_hdsize_spinbtn = sub {
2857 my $hdsize = shift;
754abb44 2858
93c8fdb0
SI
2859 $hdsize_entry_buffer //= Gtk3::EntryBuffer->new(undef, 1);
2860
2861 if (defined($hdsize)) {
d7fe65ff 2862 $hdsize_size_adj = Gtk3::Adjustment->new($config_options->{hdsize} || $hdsize, 0, $hdsize+1, 1, 1, 1);
93c8fdb0
SI
2863 } else {
2864 die "called get_hdsize_spinbtn with \$hdsize_size_adj not defined but did not pass hdsize!\n"
2865 if !defined($hdsize_size_adj);
d7fe65ff
TL
2866 }
2867
2868 my $spinbutton_hdsize = Gtk3::SpinButton->new($hdsize_size_adj, 1, 1);
2869 $spinbutton_hdsize->set_buffer($hdsize_entry_buffer);
6039e2f1 2870 $spinbutton_hdsize->set_adjustment($hdsize_size_adj);
d7fe65ff
TL
2871 $spinbutton_hdsize->set_tooltip_text("only use specified size (GB) of the harddisk (rest left unpartitioned)");
2872 return $spinbutton_hdsize;
2873};
2874
c7779156
FG
2875my $create_raid_advanced_grid = sub {
2876 my $labeled_widgets = [];
6c99667a
FG
2877 my $spinbutton_ashift = Gtk3::SpinButton->new_with_range(9,13,1);
2878 $spinbutton_ashift->set_tooltip_text("zpool ashift property (pool sector size, default 2^12)");
2879 $spinbutton_ashift->signal_connect ("value-changed" => sub {
2880 my $w = shift;
2881 $config_options->{ashift} = $w->get_value_as_int();
c7779156
FG
2882 });
2883 $config_options->{ashift} = 12 if ! defined($config_options->{ashift});
6c99667a 2884 $spinbutton_ashift->set_value($config_options->{ashift});
c7779156 2885 push @$labeled_widgets, "ashift";
6c99667a 2886 push @$labeled_widgets, $spinbutton_ashift;
c7779156
FG
2887
2888 my $combo_compress = Gtk3::ComboBoxText->new();
2889 $combo_compress->set_tooltip_text("zfs compression algorithm for rpool dataset");
2890 # note: gzip / lze not allowed for bootfs vdevs
2891 my $comp_opts = ["on","off","lzjb","lz4"];
2892 foreach my $opt (@$comp_opts) {
2893 $combo_compress->append($opt, $opt);
2894 }
2895 $config_options->{compress} = "on" if !defined($config_options->{compress});
2896 $combo_compress->set_active_id($config_options->{compress});
2897 $combo_compress->signal_connect (changed => sub {
2898 my $w = shift;
2899 $config_options->{compress} = $w->get_active_text();
2900 });
2901 push @$labeled_widgets, "compress";
2902 push @$labeled_widgets, $combo_compress;
2903
2904 my $combo_checksum = Gtk3::ComboBoxText->new();
2905 $combo_checksum->set_tooltip_text("zfs checksum algorithm for rpool dataset");
2906 my $csum_opts = ["on", "off","fletcher2", "fletcher4", "sha256"];
2907 foreach my $opt (@$csum_opts) {
2908 $combo_checksum->append($opt, $opt);
2909 }
2910 $config_options->{checksum} = "on" if !($config_options->{checksum});
2911 $combo_checksum->set_active_id($config_options->{checksum});
2912 $combo_checksum->signal_connect (changed => sub {
2913 my $w = shift;
2914 $config_options->{checksum} = $w->get_active_text();
2915 });
2916 push @$labeled_widgets, "checksum";
2917 push @$labeled_widgets, $combo_checksum;
2918
2919 my $spinbutton_copies = Gtk3::SpinButton->new_with_range(1,3,1);
2920 $spinbutton_copies->set_tooltip_text("zfs copies property for rpool dataset (in addition to RAID redundancy!)");
2921 $spinbutton_copies->signal_connect ("value-changed" => sub {
2922 my $w = shift;
2923 $config_options->{copies} = $w->get_value_as_int();
c7779156
FG
2924 });
2925 $config_options->{copies} = 1 if !defined($config_options->{copies});
2926 $spinbutton_copies->set_value($config_options->{copies});
2927 push @$labeled_widgets, "copies", $spinbutton_copies;
2928
d7fe65ff 2929 push @$labeled_widgets, "hdsize", $get_hdsize_spinbtn->();
c7779156
FG
2930 return &$create_label_widget_grid($labeled_widgets);;
2931};
2932
aed81ff0
DM
2933sub create_hdoption_view {
2934
2935 my $dialog = Gtk3::Dialog->new();
2936
2937 $dialog->set_title("Harddisk options");
2938
2939 $dialog->add_button("_OK", 1);
2940
2941 my $contarea = $dialog->get_content_area();
2942
2943 my $hbox2 = Gtk3::Box->new('horizontal', 0);
2944 $contarea->pack_start($hbox2, 1, 1, 10);
2945
2946 my $grid = Gtk3::Grid->new();
2947 $grid->set_column_spacing(10);
2948 $grid->set_row_spacing(10);
1464c7c9 2949
aed81ff0 2950 $hbox2->pack_start($grid, 1, 0, 10);
c6ed3b24
DM
2951
2952 my $row = 0;
2953
aed81ff0
DM
2954 # Filesystem type
2955
71590b6a 2956 my $label0 = Gtk3::Label->new("Filesystem");
aed81ff0 2957 $label0->set_alignment (1, 0.5);
c6ed3b24 2958 $grid->attach($label0, 0, $row, 1, 1);
1464c7c9 2959
bcbfab6b 2960 my $fstypecb = Gtk3::ComboBoxText->new();
aed81ff0 2961
121ebc59
DM
2962 my $fstype = ['ext3', 'ext4', 'xfs',
2963 'zfs (RAID0)', 'zfs (RAID1)',
2964 'zfs (RAID10)', 'zfs (RAIDZ-1)',
6f52fc3d
DM
2965 'zfs (RAIDZ-2)', 'zfs (RAIDZ-3)'];
2966
2967 push @$fstype, 'btrfs (RAID0)', 'btrfs (RAID1)', 'btrfs (RAID10)'
c20d6ab0 2968 if $setup->{enable_btrfs};
aed81ff0 2969
c6ed3b24
DM
2970 my $tcount = 0;
2971 foreach my $tmp (@$fstype) {
2972 $fstypecb->append_text($tmp);
2973 $fstypecb->set_active ($tcount)
2974 if $config_options->{filesys} eq $tmp;
2975 $tcount++;
2976 }
2977
2978 $grid->attach($fstypecb, 1, $row, 1, 1);
2979
2980 $hbox2->show_all();
2981
2982 $row++;
2983
c7779156
FG
2984 my $sep = Gtk3::HSeparator->new();
2985 $sep->set_visible(1);
2986 $grid->attach($sep, 0, $row, 2, 1);
2987 $row++;
aed81ff0 2988
f0a0d90b
TL
2989 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.");
2990 $hw_raid_note->set_line_wrap(1);
2991 $hw_raid_note->set_max_width_chars(30);
f0a0d90b
TL
2992 $hw_raid_note->set_visible(0);
2993 $grid->attach($hw_raid_note, 0, $row++, 2, 1);
2994
c7779156 2995 my $hdsize_labeled_widgets = [];
aed81ff0 2996
c7779156 2997 # size compute
c6ed3b24 2998 my $hdsize = 0;
aed81ff0
DM
2999 if ( -b $target_hd) {
3000 $hdsize = int(hd_size ($target_hd) / (1024*1024.0)); # size in GB
c6ed3b24 3001 } elsif ($target_hd) {
aed81ff0
DM
3002 $hdsize = int((-s $target_hd) / (1024*1024*1024.0));
3003 }
3004
d7fe65ff 3005 my $spinbutton_hdsize = $get_hdsize_spinbtn->($hdsize);
c7779156 3006 push @$hdsize_labeled_widgets, "hdsize", $spinbutton_hdsize;
aed81ff0
DM
3007
3008 my $entry_swapsize = Gtk3::Entry->new();
3009 $entry_swapsize->set_tooltip_text("maximum SWAP size (GB)");
3010 $entry_swapsize->signal_connect (key_press_event => \&check_float);
9bb301fb 3011 $entry_swapsize->set_text($config_options->{swapsize}) if defined($config_options->{swapsize});
c7779156 3012 push @$hdsize_labeled_widgets, "swapsize", $entry_swapsize;
aed81ff0
DM
3013
3014 my $entry_maxroot = Gtk3::Entry->new();
0adc7ca0
DM
3015 if ($setup->{product} eq 'pve') {
3016 $entry_maxroot->set_tooltip_text("maximum size (GB) for LVM root volume");
3017 $entry_maxroot->signal_connect (key_press_event => \&check_float);
3018 $entry_maxroot->set_text($config_options->{maxroot}) if $config_options->{maxroot};
3019 push @$hdsize_labeled_widgets, "maxroot", $entry_maxroot;
3020 }
aed81ff0
DM
3021
3022 my $entry_minfree = Gtk3::Entry->new();
034f75e4 3023 $entry_minfree->set_tooltip_text("minimum free LVM space (GB, required for LVM snapshots)");
aed81ff0 3024 $entry_minfree->signal_connect (key_press_event => \&check_float);
e093944c 3025 $entry_minfree->set_text($config_options->{minfree}) if defined($config_options->{minfree});
c7779156 3026 push @$hdsize_labeled_widgets, "minfree", $entry_minfree;
aed81ff0 3027
b6e875ca
DM
3028 my $entry_maxvz;
3029 if ($setup->{product} eq 'pve') {
3030 $entry_maxvz = Gtk3::Entry->new();
3031 $entry_maxvz->set_tooltip_text("maximum size (GB) for LVM data volume");
3032 $entry_maxvz->signal_connect (key_press_event => \&check_float);
2ba9752e 3033 $entry_maxvz->set_text($config_options->{maxvz}) if defined($config_options->{maxvz});
b6e875ca
DM
3034 push @$hdsize_labeled_widgets, "maxvz", $entry_maxvz;
3035 }
c7779156
FG
3036
3037 my $options_stack = Gtk3::Stack->new();
3038 $options_stack->set_visible(1);
3039 $options_stack->set_hexpand(1);
3040 $options_stack->set_vexpand(1);
3041 $options_stack->add_titled(&$create_raid_disk_grid(), "raiddisk", "Disk Setup");
3042 $options_stack->add_titled(&$create_label_widget_grid($hdsize_labeled_widgets), "hdsize", "Size Options");
3043 $options_stack->add_titled(&$create_raid_advanced_grid("zfs"), "raidzfsadvanced", "Advanced Options");
3044 $options_stack->set_visible_child_name("raiddisk");
3045 my $options_stack_switcher = Gtk3::StackSwitcher->new();
3046 $options_stack_switcher->set_halign('center');
3047 $options_stack_switcher->set_stack($options_stack);
3048 $grid->attach($options_stack_switcher, 0, $row, 2, 1);
3049 $row++;
3050 $grid->attach($options_stack, 0, $row, 2, 1);
c6ed3b24 3051 $row++;
aed81ff0 3052
bd3a2e26 3053 $hdoption_first_setup = 0;
c7779156
FG
3054
3055 my $switch_view = sub {
3056 my $raid = $config_options->{filesys} =~ m/zfs|btrfs/;
3057 my $enable_zfs_opts = $config_options->{filesys} =~ m/zfs/;
c6ed3b24 3058
c7779156
FG
3059 $target_hd_combo->set_visible(!$raid);
3060 $options_stack->get_child_by_name("hdsize")->set_visible(!$raid);
3061 $options_stack->get_child_by_name("raiddisk")->set_visible($raid);
f0a0d90b 3062 $hw_raid_note->set_visible($raid);
c7779156
FG
3063 $options_stack_switcher->set_visible($enable_zfs_opts);
3064 $options_stack->get_child_by_name("raidzfsadvanced")->set_visible($enable_zfs_opts);
3065 if ($raid) {
c6ed3b24 3066 $target_hd_label->set_text("Target: $config_options->{filesys} ");
c7779156 3067 $options_stack->set_visible_child_name("raiddisk");
c6ed3b24 3068 } else {
c6ed3b24
DM
3069 $target_hd_label->set_text("Target Harddisk: ");
3070 }
c7779156
FG
3071 my (undef, $pref_width) = $dialog->get_preferred_width();
3072 my (undef, $pref_height) = $dialog->get_preferred_height();
650a9aab 3073 $pref_height = 750 if $pref_height > 750;
c7779156 3074 $dialog->resize($pref_width, $pref_height);
f7b853d1
DM
3075 };
3076
c7779156 3077 &$switch_view();
f7b853d1
DM
3078
3079 $fstypecb->signal_connect (changed => sub {
3080 $config_options->{filesys} = $fstypecb->get_active_text();
c7779156 3081 &$switch_view();
f7b853d1
DM
3082 });
3083
95844cc6
TL
3084 my $sep2 = Gtk3::HSeparator->new();
3085 $sep2->set_visible(1);
3086 $contarea->pack_end($sep2, 1, 1, 10);
3087
c6ed3b24 3088 $dialog->show();
aed81ff0
DM
3089
3090 $dialog->run();
3091
3092 my $get_float = sub {
3093 my ($entry) = @_;
3094
3095 my $text = $entry->get_text();
3096 return undef if !defined($text);
3097
3098 $text =~ s/^\s+//;
3099 $text =~ s/\s+$//;
3100
3101 return undef if $text !~ m/^\d+(\.\d+)?$/;
3102
3103 return $text;
3104 };
3105
3106 my $tmp;
3107
3108 if (($tmp = &$get_float($spinbutton_hdsize)) && ($tmp != $hdsize)) {
3109 $config_options->{hdsize} = $tmp;
3110 } else {
3111 delete $config_options->{hdsize};
3112 }
3113
3114 if (defined($tmp = &$get_float($entry_swapsize))) {
3115 $config_options->{swapsize} = $tmp;
3116 } else {
3117 delete $config_options->{swapsize};
3118 }
3119
3120 if (defined($tmp = &$get_float($entry_maxroot))) {
3121 $config_options->{maxroot} = $tmp;
3122 } else {
3123 delete $config_options->{maxroot};
3124 }
3125
3126 if (defined($tmp = &$get_float($entry_minfree))) {
3127 $config_options->{minfree} = $tmp;
3128 } else {
3129 delete $config_options->{minfree};
3130 }
3131
b6e875ca 3132 if ($entry_maxvz && defined($tmp = &$get_float($entry_maxvz))) {
aed81ff0
DM
3133 $config_options->{maxvz} = $tmp;
3134 } else {
3135 delete $config_options->{maxvz};
3136 }
3137
3138 $dialog->destroy();
3139}
3140
121ebc59 3141my $get_raid_devlist = sub {
c6ed3b24
DM
3142
3143 my $dev_name_hash = {};
3144
3145 my $devlist = [];
5f8e86d5 3146 for (my $i = 0; $i < @$hds; $i++) {
c6ed3b24 3147 if (my $hd = $config_options->{"disksel$i"}) {
17fd908e 3148 my ($disk, $devname, $size, $model, $logical_bsize) = @$hd;
1464c7c9 3149 die "device '$devname' is used more than once\n"
c6ed3b24
DM
3150 if $dev_name_hash->{$devname};
3151 $dev_name_hash->{$devname} = $hd;
3152 push @$devlist, $hd;
3153 }
3154 }
3155
121ebc59
DM
3156 return $devlist;
3157};
3158
14aacec8
FG
3159sub zfs_mirror_size_check {
3160 my ($expected, $actual) = @_;
3161
3162 die "mirrored disks must have same size\n"
3163 if abs($expected - $actual) > $expected / 10;
3164}
3165
5ea943cf
SI
3166sub legacy_bios_4k_check {
3167 my ($lbs) = @_;
3168 die "Booting from 4kn drive in legacy BIOS mode is not supported.\n"
3169 if (($boot_type ne 'efi') && ($lbs == 4096));
3170}
3171
121ebc59
DM
3172sub get_zfs_raid_setup {
3173
3174 my $filesys = $config_options->{filesys};
3175
3176 my $devlist = &$get_raid_devlist();
3177
224bb7b0 3178 my $diskcount = scalar(@$devlist);
0cfa502c 3179 die "$filesys needs at least one device\n" if $diskcount < 1;
c6ed3b24 3180
121ebc59
DM
3181 my $bootdevlist = [];
3182
c6ed3b24
DM
3183 my $cmd= '';
3184 if ($filesys eq 'zfs (RAID0)') {
3185 push @$bootdevlist, @$devlist[0];
3186 foreach my $hd (@$devlist) {
5ea943cf 3187 legacy_bios_4k_check(@$hd[4]);
c6ed3b24
DM
3188 $cmd .= " @$hd[1]";
3189 }
3190 } elsif ($filesys eq 'zfs (RAID1)') {
0cfa502c 3191 die "zfs (RAID1) needs at least 2 device\n" if $diskcount < 2;
c6ed3b24 3192 $cmd .= ' mirror ';
269c66a6 3193 my $hd = @$devlist[0];
14aacec8 3194 my $expected_size = @$hd[2]; # all disks need approximately same size
269c66a6 3195 foreach $hd (@$devlist) {
14aacec8 3196 zfs_mirror_size_check($expected_size, @$hd[2]);
5ea943cf 3197 legacy_bios_4k_check(@$hd[4]);
c6ed3b24
DM
3198 $cmd .= " @$hd[1]";
3199 push @$bootdevlist, $hd;
3200 }
3201 } elsif ($filesys eq 'zfs (RAID10)') {
0cfa502c 3202 die "zfs (RAID10) needs at least 4 device\n" if $diskcount < 4;
b8f4f0f9 3203 die "zfs (RAID10) needs an even number of devices\n" if $diskcount & 1;
1464c7c9 3204
c6ed3b24
DM
3205 push @$bootdevlist, @$devlist[0], @$devlist[1];
3206
224bb7b0 3207 for (my $i = 0; $i < $diskcount; $i+=2) {
c6ed3b24
DM
3208 my $hd1 = @$devlist[$i];
3209 my $hd2 = @$devlist[$i+1];
14aacec8 3210 zfs_mirror_size_check(@$hd1[2], @$hd2[2]); # pairs need approximately same size
5ea943cf
SI
3211 legacy_bios_4k_check(@$hd1[4]);
3212 legacy_bios_4k_check(@$hd2[4]);
c6ed3b24
DM
3213 $cmd .= ' mirror ' . @$hd1[1] . ' ' . @$hd2[1];
3214 }
3215
3216 } elsif ($filesys =~ m/^zfs \(RAIDZ-([123])\)$/) {
3217 my $level = $1;
3218 my $mindisks = 2 + $level;
0cfa502c 3219 die "zfs (RAIDZ-$level) needs at least $mindisks devices\n" if scalar(@$devlist) < $mindisks;
269c66a6 3220 my $hd = @$devlist[0];
14aacec8 3221 my $expected_size = @$hd[2]; # all disks need approximately same size
097ecf8f 3222 $cmd .= " raidz$level";
269c66a6 3223 foreach $hd (@$devlist) {
14aacec8 3224 zfs_mirror_size_check($expected_size, @$hd[2]);
5ea943cf 3225 legacy_bios_4k_check(@$hd[4]);
c6ed3b24
DM
3226 $cmd .= " @$hd[1]";
3227 push @$bootdevlist, $hd;
3228 }
3229 } else {
3230 die "unknown zfs mode '$filesys'\n";
3231 }
3232
3233 return ($devlist, $bootdevlist, $cmd);
3234}
3235
121ebc59
DM
3236sub get_btrfs_raid_setup {
3237
3238 my $filesys = $config_options->{filesys};
3239
3240 my $devlist = &$get_raid_devlist();
3241
3242 my $diskcount = scalar(@$devlist);
0cfa502c 3243 die "$filesys needs at least one device\n" if $diskcount < 1;
121ebc59
DM
3244
3245 my $mode;
3246
3247 if ($diskcount == 1) {
3248 $mode = 'single';
3249 } else {
3250 if ($filesys eq 'btrfs (RAID0)') {
3251 $mode = 'raid0';
3252 } elsif ($filesys eq 'btrfs (RAID1)') {
0cfa502c 3253 die "btrfs (RAID1) needs at least 2 device\n" if $diskcount < 2;
121ebc59
DM
3254 $mode = 'raid1';
3255 } elsif ($filesys eq 'btrfs (RAID10)') {
0cfa502c 3256 die "btrfs (RAID10) needs at least 4 device\n" if $diskcount < 4;
121ebc59
DM
3257 $mode = 'raid10';
3258 } else {
9d69f3d3 3259 die "unknown btrfs mode '$filesys'\n";
121ebc59
DM
3260 }
3261 }
3262
3263 return ($devlist, $mode);
3264}
3265
218a4b6b 3266my $last_hd_selected = 0;
89a12446
DM
3267sub create_hdsel_view {
3268
451b1da5 3269 $prev_btn->set_sensitive(1); # enable previous button at this point
201a5120 3270
71590b6a 3271 cleanup_view();
89a12446 3272
71590b6a
OB
3273 my $vbox = Gtk3::VBox->new(0, 0);
3274 $inbox->pack_start($vbox, 1, 0, 0);
3275 my $hbox = Gtk3::HBox->new(0, 0);
3276 $vbox->pack_start($hbox, 0, 0, 10);
968fa90b 3277
17fd908e 3278 my ($disk, $devname, $size, $model, $logical_bsize) = @{@$hds[0]};
9227a70f 3279 $target_hd = $devname if !defined($target_hd);
89a12446 3280
71590b6a
OB
3281 $target_hd_label = Gtk3::Label->new("Target Harddisk: ");
3282 $hbox->pack_start($target_hd_label, 0, 0, 0);
89a12446 3283
bcbfab6b 3284 $target_hd_combo = Gtk3::ComboBoxText->new();
89a12446 3285
1aa5bd02 3286 foreach my $hd (@$hds) {
17fd908e 3287 ($disk, $devname, $size, $model, $logical_bsize) = @$hd;
71590b6a 3288 $target_hd_combo->append_text (get_device_desc($devname, $size, $model));
1aa5bd02 3289 }
89a12446 3290
90af1603
OB
3291 my $raid = $config_options->{filesys} =~ m/zfs|btrfs/;
3292 if ($raid) {
3293 $target_hd_label->set_text("Target: $config_options->{filesys} ");
3294 $target_hd_combo->set_visible(0);
3295 $target_hd_combo->set_no_show_all(1);
3296 }
218a4b6b 3297 $target_hd_combo->set_active($last_hd_selected);
71590b6a 3298 $target_hd_combo->signal_connect(changed => sub {
1aa5bd02
DM
3299 $a = shift->get_active;
3300 my ($disk, $devname) = @{@$hds[$a]};
3b959bef 3301 $last_hd_selected = $a;
1aa5bd02 3302 $target_hd = $devname;
1aa5bd02 3303 });
1464c7c9 3304
71590b6a 3305 $hbox->pack_start($target_hd_combo, 0, 0, 10);
aed81ff0 3306
71590b6a 3307 my $options = Gtk3::Button->new('_Options');
aed81ff0
DM
3308 $options->signal_connect (clicked => \&create_hdoption_view);
3309 $hbox->pack_start ($options, 0, 0, 0);
3310
89a12446
DM
3311
3312 $inbox->show_all;
3313
201a5120 3314 display_html();
c6ed3b24 3315
71590b6a 3316 set_next(undef, sub {
c6ed3b24
DM
3317
3318 if ($config_options->{filesys} =~ m/zfs/) {
a7d40341 3319 my ($devlist) = eval { get_zfs_raid_setup() };
c6ed3b24 3320 if (my $err = $@) {
303dfb2c
TL
3321 display_message("Warning: $err\nPlease fix ZFS setup first.");
3322 return;
c6ed3b24 3323 }
303dfb2c 3324 $config_options->{target_hds} = [ map { $_->[1] } @$devlist ];
121ebc59 3325 } elsif ($config_options->{filesys} =~ m/btrfs/) {
a7d40341 3326 my ($devlist) = eval { get_btrfs_raid_setup() };
121ebc59 3327 if (my $err = $@) {
303dfb2c
TL
3328 display_message("Warning: $err\nPlease fix BTRFS setup first.");
3329 return;
121ebc59 3330 }
303dfb2c 3331 $config_options->{target_hds} = [ map { $_->[1] } @$devlist ];
c6ed3b24 3332 } else {
5ea943cf
SI
3333 eval { legacy_bios_4k_check(logical_blocksize($target_hd)) };
3334 if (my $err = $@) {
3335 display_message("Warning: $err\n");
3336 return;
3337 }
a7d40341 3338 $config_options->{target_hds} = [ $target_hd ];
c6ed3b24 3339 }
303dfb2c
TL
3340
3341 $step_number++;
3342 create_country_view();
c6ed3b24 3343 });
89a12446
DM
3344}
3345
3346sub create_extract_view {
3347
71590b6a 3348 cleanup_view();
89a12446 3349
550958aa
DM
3350 display_info();
3351
201a5120 3352 $next->set_sensitive(0);
ac3ee85b
TL
3353 $prev_btn->set_sensitive(0);
3354 $prev_btn->hide();
89a12446 3355
71590b6a 3356 my $vbox = Gtk3::VBox->new(0, 0);
89a12446 3357 $inbox->pack_start ($vbox, 1, 0, 0);
71590b6a 3358 my $hbox = Gtk3::HBox->new(0, 0);
53986d77 3359 $vbox->pack_start ($hbox, 0, 0, 10);
89a12446 3360
71590b6a 3361 my $vbox2 = Gtk3::VBox->new(0, 0);
89a12446
DM
3362 $hbox->pack_start ($vbox2, 0, 0, 0);
3363
71590b6a 3364 $progress_status = Gtk3::Label->new('');
89a12446 3365 $vbox2->pack_start ($progress_status, 1, 1, 0);
968fa90b 3366
7becc472 3367 $progress = Gtk3::ProgressBar->new;
45feca6f 3368 $progress->set_show_text(1);
7becc472 3369 $progress->set_size_request (600, -1);
89a12446 3370
71590b6a 3371 $vbox2->pack_start($progress, 0, 0, 0);
89a12446 3372
201a5120 3373 $inbox->show_all();
89a12446
DM
3374
3375 my $tdir = $opt_testmode ? "target" : "/target";
3376 mkdir $tdir;
97980bf2 3377 my $base = "${proxmox_cddir}/$setup->{product}-base.squashfs";
89a12446 3378
71590b6a 3379 eval { extract_data($base, $tdir); };
89a12446
DM
3380 my $err = $@;
3381
201a5120 3382 $next->set_sensitive(1);
89a12446 3383
71590b6a 3384 set_next("_Reboot", sub { exit (0); } );
89a12446 3385
296cf41f 3386 if ($err) {
201a5120
OB
3387 display_html("fail.htm");
3388 display_error($err);
296cf41f 3389 } else {
201a5120
OB
3390 cleanup_view();
3391 display_html("success.htm");
296cf41f 3392 }
89a12446
DM
3393}
3394
89a12446
DM
3395sub create_intro_view {
3396
451b1da5 3397 $prev_btn->set_sensitive(0);
201a5120
OB
3398
3399 cleanup_view();
89a12446 3400
ca951e77 3401 if (int($total_memory) < 1024) {
18a9811e
TL
3402 my $fullname = $product_fullname->{$setup->{product}};
3403
3befbf97 3404 display_error("Less than 1 GiB of usable memory detected, installation will probably fail.\n\n".
18a9811e 3405 "See 'System Requirements' in the $fullname documentation.");
2b85ee1b
OB
3406 }
3407
bdeca872
DM
3408 if ($setup->{product} eq 'pve') {
3409 eval {
3410 my $cpuinfo = file_get_contents('/proc/cpuinfo');
3411 if ($cpuinfo && !($cpuinfo =~ /^flags\s*:.*(vmx|svm)/m)) {
2780ea4f 3412 display_error("No support for KVM virtualization detected.\n\n" .
bdeca872
DM
3413 "Check BIOS settings for Intel VT / AMD-V / SVM.")
3414 }
3415 };
3416 }
7fff0d85 3417
201a5120 3418 display_html();
89a12446 3419
201a5120 3420 $step_number++;
71590b6a 3421 set_next("I a_gree", \&create_hdsel_view);
89a12446
DM
3422}
3423
71590b6a 3424$ipconf = get_ip_config();
89a12446 3425
9d1f1ee3 3426$country = detect_country() if $ipconf->{default} || $opt_testmode;
89a12446
DM
3427
3428# read country, kmap and timezone infos
71590b6a 3429$cmap = read_cmap();
89a12446 3430
9d1f1ee3
FG
3431if (!defined($cmap->{country}->{$country})) {
3432 print $logfd "ignoring detected country '$country', invalid or unknown\n";
3433 $country = undef;
3434}
3435
89a12446
DM
3436create_main_window ();
3437
ff2ce71c
FG
3438my $initial_error = 0;
3439
89a12446
DM
3440if (!defined ($hds) || (scalar (@$hds) <= 0)) {
3441 print "no hardisks found\n";
ff2ce71c 3442 $initial_error = 1;
201a5120 3443 display_html("nohds.htm");
71590b6a 3444 set_next("Reboot", sub { exit(0); } );
89a12446 3445} else {
89a12446
DM
3446 foreach my $hd (@$hds) {
3447 my ($disk, $devname) = @$hd;
3448 next if $devname =~ m|^/dev/md\d+$|;
3449 print "found Disk$disk N:$devname\n";
3450 }
89a12446
DM
3451}
3452
72836708
FG
3453if (!$initial_error && (scalar keys %{ $ipconf->{ifaces} } == 0)) {
3454 print "no network interfaces found\n";
3455 $initial_error = 1;
201a5120 3456 display_html("nonics.htm");
71590b6a 3457 set_next("Reboot", sub { exit(0); } );
72836708
FG
3458}
3459
ff2ce71c
FG
3460create_intro_view () if !$initial_error;
3461
7becc472 3462Gtk3->main;
89a12446
DM
3463
3464exit 0;