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