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