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