]> git.proxmox.com Git - pve-installer.git/blame - proxinstall
use pve-efiboot-tool for systemd boot ESP preparation
[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
DM
1870 $data =~ s/__LICENSE_TITLE__/$title/;
1871 }
1872
ed0e6aea 1873 $htmlview->load_html($data, $url);
550958aa
DM
1874
1875 $last_display_change = time();
7becc472
DM
1876}
1877
201a5120
OB
1878sub prev_function {
1879
1880 my ($text, $fctn) = @_;
1881
1882 $fctn = $step_number if !$fctn;
1883 $text = "_Previous" if !$text;
451b1da5 1884 $prev_btn->set_label ($text);
201a5120
OB
1885
1886 $step_number--;
1887 $steps[$step_number]->{function}();
1888
71590b6a 1889 $prev_btn->grab_focus();
201a5120
OB
1890}
1891
89a12446
DM
1892sub set_next {
1893 my ($text, $fctn) = @_;
1894
1895 $next_fctn = $fctn;
201a5120
OB
1896 my $step = $steps[$step_number];
1897 $text //= $steps[$step_number]->{next_button} // '_Next';
71590b6a 1898 $next->set_label($text);
968fa90b 1899
71590b6a 1900 $next->grab_focus();
89a12446 1901}
89a12446
DM
1902
1903sub create_main_window {
1904
71590b6a
OB
1905 $window = Gtk3::Window->new();
1906 $window->set_default_size(1024, 768);
84761f93 1907 $window->set_has_resize_grip(0);
71590b6a 1908 $window->set_decorated(0) if !$opt_testmode;
89a12446 1909
71590b6a 1910 my $vbox = Gtk3::VBox->new(0, 0);
89a12446 1911
782b4acd
DM
1912 my $logofn = "$setup->{product}-banner.png";
1913 my $image = Gtk3::Image->new_from_file("${proxmox_libdir}/$logofn");
71590b6a 1914 $vbox->pack_start($image, 0, 0, 0);
89a12446 1915
71590b6a
OB
1916 my $hbox = Gtk3::HBox->new(0, 0);
1917 $vbox->pack_start($hbox, 1, 1, 0);
89a12446 1918
7becc472
DM
1919 # my $f1 = Gtk3::Frame->new ('test');
1920 # $f1->set_shadow_type ('none');
1921 # $hbox->pack_start ($f1, 1, 1, 0);
89a12446 1922
71590b6a
OB
1923 my $sep1 = Gtk3::HSeparator->new();
1924 $vbox->pack_start($sep1, 0, 0, 0);
89a12446 1925
71590b6a
OB
1926 $cmdbox = Gtk3::HBox->new();
1927 $vbox->pack_start($cmdbox, 0, 0, 10);
89a12446 1928
71590b6a
OB
1929 $next = Gtk3::Button->new('_Next');
1930 $next->signal_connect(clicked => sub { $last_display_change = 0; &$next_fctn (); });
1931 $cmdbox->pack_end($next, 0, 0, 10);
201a5120
OB
1932
1933
71590b6a
OB
1934 $prev_btn = Gtk3::Button->new('_Previous');
1935 $prev_btn->signal_connect(clicked => sub { $last_display_change = 0; &prev_function (); });
1936 $cmdbox->pack_end($prev_btn, 0, 0, 10);
201a5120
OB
1937
1938
71590b6a
OB
1939 my $abort = Gtk3::Button->new('_Abort');
1940 $abort->set_can_focus(0);
1941 $cmdbox->pack_start($abort, 0, 0, 10);
1942 $abort->signal_connect(clicked => sub { exit (-1); });
89a12446 1943
71590b6a
OB
1944 my $vbox2 = Gtk3::VBox->new(0, 0);
1945 $hbox->add($vbox2);
89a12446 1946
ed0e6aea 1947 $htmlview = Gtk3::WebKit2::WebView->new();
7becc472
DM
1948 my $scrolls = Gtk3::ScrolledWindow->new();
1949 $scrolls->add($htmlview);
1464c7c9 1950
71590b6a
OB
1951 my $hbox2 = Gtk3::HBox->new(0, 0);
1952 $hbox2->pack_start($scrolls, 1, 1, 0);
89a12446 1953
71590b6a 1954 $vbox2->pack_start($hbox2, 1, 1, 0);
89a12446 1955
71590b6a
OB
1956 my $vbox3 = Gtk3::VBox->new(0, 0);
1957 $vbox2->pack_start($vbox3, 0, 0, 0);
89a12446 1958
7becc472 1959 my $sep2 = Gtk3::HSeparator->new;
71590b6a 1960 $vbox3->pack_start($sep2, 0, 0, 0);
89a12446 1961
71590b6a
OB
1962 $inbox = Gtk3::HBox->new(0, 0);
1963 $vbox3->pack_start($inbox, 0, 0, 0);
89a12446 1964
71590b6a 1965 $window->add($vbox);
89a12446
DM
1966
1967 $window->show_all;
71590b6a 1968 $window->realize();
89a12446
DM
1969}
1970
1464c7c9 1971sub cleanup_view {
d2120e51
DM
1972 $inbox->foreach(sub {
1973 my $child = shift;
1464c7c9 1974 $inbox->remove ($child);
d2120e51 1975 });
89a12446
DM
1976}
1977
aed81ff0
DM
1978# fixme: newer GTK3 has special properties to handle numbers with Entry
1979# only allow floating point numbers with Gtk3::Entry
e73c5fcf 1980
aed81ff0
DM
1981sub check_float {
1982 my ($entry, $event) = @_;
1983
e73c5fcf
FG
1984 return check_number($entry, $event, 1);
1985}
1986
1987sub check_int {
1988 my ($entry, $event) = @_;
1989
1990 return check_number($entry, $event, 0);
1991}
1992
1993sub check_number {
1994 my ($entry, $event, $float) = @_;
aed81ff0
DM
1995
1996 my $val = $event->get_keyval;
1997
e73c5fcf 1998 if (($float && $val == ord '.') ||
aed81ff0
DM
1999 $val == Gtk3::Gdk::KEY_ISO_Left_Tab ||
2000 $val == Gtk3::Gdk::KEY_Shift_L ||
2001 $val == Gtk3::Gdk::KEY_Tab ||
2002 $val == Gtk3::Gdk::KEY_Left ||
2003 $val == Gtk3::Gdk::KEY_Right ||
2004 $val == Gtk3::Gdk::KEY_BackSpace ||
2005 $val == Gtk3::Gdk::KEY_Delete ||
2006 ($val >= ord '0' && $val <= ord '9') ||
2007 ($val >= Gtk3::Gdk::KEY_KP_0 &&
2008 $val <= Gtk3::Gdk::KEY_KP_9)) {
2009 return undef;
2010 }
2011
2012 return 1;
2013}
2014
d2120e51 2015sub create_text_input {
89a12446
DM
2016 my ($default, $text) = @_;
2017
71590b6a 2018 my $hbox = Gtk3::HBox->new(0, 0);
89a12446 2019
71590b6a
OB
2020 my $label = Gtk3::Label->new($text);
2021 $label->set_size_request(150, -1);
2022 $label->set_alignment(1, 0.5);
2023 $hbox->pack_start($label, 0, 0, 10);
2024 my $e1 = Gtk3::Entry->new();
2025 $e1->set_width_chars(30);
2026 $hbox->pack_start($e1, 0, 0, 0);
2027 $e1->set_text($default);
89a12446
DM
2028
2029 return ($hbox, $e1);
2030}
2031
89a12446
DM
2032sub get_ip_config {
2033
fe44bd92
FG
2034 my $ifaces = {};
2035 my $default;
89a12446 2036
fe44bd92
FG
2037 my $links = `ip -o l`;
2038 foreach my $l (split /\n/,$links) {
2039 my ($index, $name, $flags, $state, $mac) = $l =~ m/^(\d+):\s+(\S+):\s+<(\S+)>.*\s+state\s+(\S+)\s+.*\s+link\/ether\s+(\S+)\s+/;
2040 next if !$name || $name eq 'lo';
89a12446 2041
fe44bd92
FG
2042 my $driver = readlink "/sys/class/net/$name/device/driver" || 'unknown';
2043 $driver =~ s!^.*/!!;
2044
2045 $ifaces->{"$index"} = {
2046 name => $name,
2047 driver => $driver,
2048 flags => $flags,
2049 state => $state,
2050 mac => $mac,
2051 };
2052
2053 my $addresses = `ip -o a s $name`;
2054 foreach my $a (split /\n/,$addresses) {
2055 my ($family, $ip, $prefix) = $a =~ m/^\Q$index\E:\s+\Q$name\E\s+(inet|inet6)\s+($IPRE)\/(\d+)\s+/;
2056 next if !$ip;
32b6fbcf 2057 next if $a =~ /scope\s+link/; # ignore link local
fe44bd92
FG
2058
2059 my $mask = $prefix;
2060
2061 if ($family eq 'inet') {
2062 next if !$ip =~ /$IPV4RE/;
2063 next if $prefix < 8 || $prefix > 32;
2064 $mask = @$ipv4_reverse_mask[$prefix];
2065 } else {
2066 next if !$ip =~ /$IPV6RE/;
2067 }
2068
2069 $default = $index if !$default;
2070
2071 $ifaces->{"$index"}->{"$family"} = {
2072 mask => $mask,
2073 addr => $ip,
2074 };
2075 }
2076 }
2077
2078
2079 my $route = `ip route`;
2080 my ($gateway) = $route =~ m/^default\s+via\s+(\S+)\s+/m;
89a12446
DM
2081
2082 my $resolvconf = `cat /etc/resolv.conf`;
2083 my ($dnsserver) = $resolvconf =~ m/^nameserver\s+(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})$/m;
713790a4 2084 my ($domain) = $resolvconf =~ m/^domain\s+(\S+)$/m;
89a12446
DM
2085
2086 return {
fe44bd92
FG
2087 default => $default,
2088 ifaces => $ifaces,
89a12446
DM
2089 gateway => $gateway,
2090 dnsserver => $dnsserver,
713790a4 2091 domain => $domain,
89a12446
DM
2092 }
2093}
2094
2095sub display_message {
2096 my ($msg) = @_;
2097
71590b6a
OB
2098 my $dialog = Gtk3::MessageDialog->new($window, 'modal',
2099 'info', 'ok', $msg);
89a12446
DM
2100 $dialog->run();
2101 $dialog->destroy();
2102}
2103
2104sub display_error {
2105 my ($msg) = @_;
2106
71590b6a
OB
2107 my $dialog = Gtk3::MessageDialog->new($window, 'modal',
2108 'error', 'ok', $msg);
89a12446
DM
2109 $dialog->run();
2110 $dialog->destroy();
2111}
2112
fe44bd92
FG
2113my $ipconf_first_view = 1;
2114
89a12446
DM
2115sub create_ipconf_view {
2116
201a5120
OB
2117 cleanup_view();
2118 display_html();
89a12446 2119
71590b6a
OB
2120 my $vbox = Gtk3::VBox->new(0, 0);
2121 $inbox->pack_start($vbox, 1, 0, 0);
2122 my $hbox = Gtk3::HBox->new(0, 0);
2123 $vbox->pack_start($hbox, 0, 0, 10);
2124 my $vbox2 = Gtk3::VBox->new(0, 0);
2125 $hbox->add($vbox2);
89a12446 2126
ebc4f76f 2127 my $ipaddr_text = $config->{ipaddress} // "192.168.100.2";
fe44bd92
FG
2128 my $ipbox;
2129 ($ipbox, $ipconf_entry_addr) =
71590b6a 2130 create_text_input($ipaddr_text, 'IP Address:');
fe44bd92 2131
ebc4f76f 2132 my $netmask_text = $config->{netmask} // "255.255.255.0";
fe44bd92
FG
2133 my $maskbox;
2134 ($maskbox, $ipconf_entry_mask) =
71590b6a 2135 create_text_input($netmask_text, 'Netmask:');
fe44bd92
FG
2136
2137 my $device_cb = Gtk3::ComboBoxText->new();
2138 $device_cb->set_active(0);
2139 $device_cb->set_visible(1);
2140
2141 my $get_device_desc = sub {
2142 my $iface = shift;
2143 return "$iface->{name} - $iface->{mac} ($iface->{driver})";
2144 };
2145
2146 my $device_active_map = {};
ebc4f76f 2147 my $device_active_reverse_map = {};
5b6ba737
FG
2148
2149 my $device_change_handler = sub {
2150 my $current = shift;
d6524c52
TL
2151
2152 my $new = $device_active_map->{$current->get_active()};
cd2d2a27 2153 return if defined($ipconf->{selected}) && $new eq $ipconf->{selected};
d6524c52
TL
2154
2155 $ipconf->{selected} = $new;
5b6ba737 2156 my $iface = $ipconf->{ifaces}->{$ipconf->{selected}};
ebc4f76f 2157 $config->{mngmt_nic} = $iface->{name};
5b6ba737
FG
2158 $ipconf_entry_addr->set_text($iface->{inet}->{addr} || $iface->{inet6}->{addr})
2159 if $iface->{inet}->{addr} || $iface->{inet6}->{addr};
2160 $ipconf_entry_mask->set_text($iface->{inet}->{mask} || $iface->{inet6}->{mask})
2161 if $iface->{inet}->{mask} || $iface->{inet6}->{mask};
2162 };
2163
fe44bd92
FG
2164 my $i = 0;
2165 foreach my $index (sort keys %{$ipconf->{ifaces}}) {
2166 $device_cb->append_text(&$get_device_desc($ipconf->{ifaces}->{$index}));
ebc4f76f
TL
2167 $device_active_map->{$i} = $index;
2168 $device_active_reverse_map->{$ipconf->{ifaces}->{$index}->{name}} = $i;
fe44bd92
FG
2169 if ($ipconf_first_view && $index == $ipconf->{default}) {
2170 $device_cb->set_active($i);
5b6ba737 2171 &$device_change_handler($device_cb);
fe44bd92
FG
2172 $ipconf_first_view = 0;
2173 }
71590b6a 2174 $device_cb->signal_connect('changed' => $device_change_handler);
fe44bd92
FG
2175 $i++;
2176 }
2177
ebc4f76f
TL
2178 if (my $nic = $config->{mngmt_nic}) {
2179 $device_cb->set_active($device_active_reverse_map->{$nic} // 0);
2180 } else {
2181 $device_cb->set_active(0);
2182 }
5b6ba737 2183
71590b6a
OB
2184 my $devicebox = Gtk3::HBox->new(0, 0);
2185 my $label = Gtk3::Label->new("Management Interface:");
2186 $label->set_size_request(150, -1);
2187 $label->set_alignment(1, 0.5);
2188 $devicebox->pack_start($label, 0, 0, 10);
2189 $devicebox->pack_start($device_cb, 0, 0, 0);
fe44bd92 2190
71590b6a 2191 $vbox2->pack_start($devicebox, 0, 0, 2);
968fa90b 2192
ebc4f76f 2193 my $hn = $config->{fqdn} // "$setup->{product}." . ($ipconf->{domain} // "example.invalid");
1464c7c9 2194
968fa90b 2195 my ($hostbox, $hostentry) =
71590b6a
OB
2196 create_text_input($hn, 'Hostname (FQDN):');
2197 $vbox2->pack_start($hostbox, 0, 0, 2);
89a12446 2198
71590b6a 2199 $vbox2->pack_start($ipbox, 0, 0, 2);
89a12446 2200
71590b6a 2201 $vbox2->pack_start($maskbox, 0, 0, 2);
89a12446 2202
ebc4f76f 2203 $gateway = $config->{gateway} // $ipconf->{gateway} || '192.168.100.1';
89a12446
DM
2204
2205 my $gwbox;
d2120e51 2206 ($gwbox, $ipconf_entry_gw) =
71590b6a 2207 create_text_input($gateway, 'Gateway:');
89a12446 2208
71590b6a 2209 $vbox2->pack_start($gwbox, 0, 0, 2);
89a12446 2210
ebc4f76f 2211 $dnsserver = $config->{dnsserver} // $ipconf->{dnsserver} || $gateway;
89a12446
DM
2212
2213 my $dnsbox;
d2120e51 2214 ($dnsbox, $ipconf_entry_dns) =
71590b6a 2215 create_text_input($dnsserver, 'DNS Server:');
89a12446 2216
71590b6a 2217 $vbox2->pack_start($dnsbox, 0, 0, 0);
89a12446
DM
2218
2219 $inbox->show_all;
71590b6a 2220 set_next(undef, sub {
d2120e51
DM
2221
2222 # verify hostname
1464c7c9 2223
89a12446 2224 my $text = $hostentry->get_text();
968fa90b 2225
89a12446
DM
2226 $text =~ s/^\s+//;
2227 $text =~ s/\s+$//;
2228
ebc4f76f
TL
2229 $config->{fqdn} = $text;
2230
ac3757a9 2231 my $namere = "([a-zA-Z0-9]([a-zA-Z0-9\-]*[a-zA-Z0-9])?)";
968fa90b 2232
24973868
WB
2233 # Debian does not support purely numeric hostnames
2234 if ($text && $text =~ /^[0-9]+(?:\.|$)/) {
2235 display_message("Purely numeric hostnames are not allowed.");
2236 $hostentry->grab_focus();
2237 return;
2238 }
2239
a39bc1f2 2240 if ($text && $text =~ m/^(${namere}\.)*${namere}$/ && $text !~ m/.example.invalid$/ &&
89a12446
DM
2241 $text =~ m/^([^\.]+)\.(\S+)$/) {
2242 $hostname = $1;
2243 $domain = $2;
d2120e51 2244 } else {
71590b6a 2245 display_message("Hostname does not look like a fully qualified domain name.");
d2120e51 2246 $hostentry->grab_focus();
89a12446
DM
2247 return;
2248 }
d2120e51
DM
2249
2250 # verify ip address
2251
2252 $text = $ipconf_entry_addr->get_text();
2253 $text =~ s/^\s+//;
2254 $text =~ s/\s+$//;
2255 if ($text =~ m!^($IPV4RE)$!) {
2256 $ipaddress = $text;
b6200603
DM
2257 $ipversion = 4;
2258 } elsif ($text =~ m!^($IPV6RE)$!) {
1464c7c9 2259 $ipaddress = $text;
b6200603 2260 $ipversion = 6;
d2120e51 2261 } else {
71590b6a 2262 display_message("IP address is not valid.");
d2120e51
DM
2263 $ipconf_entry_addr->grab_focus();
2264 return;
2265 }
ebc4f76f 2266 $config->{ipaddress} = $ipaddress;
d2120e51
DM
2267
2268 $text = $ipconf_entry_mask->get_text();
2269 $text =~ s/^\s+//;
2270 $text =~ s/\s+$//;
b6200603
DM
2271 if (($ipversion == 6) && ($text =~ m/^(\d+)$/) && ($1 >= 8) && ($1 <= 126)) {
2272 $netmask = $text;
2273 } elsif (($ipversion == 4) && defined($ipv4_mask_hash->{$text})) {
d2120e51
DM
2274 $netmask = $text;
2275 } else {
71590b6a 2276 display_message("Netmask is not valid.");
d2120e51
DM
2277 $ipconf_entry_mask->grab_focus();
2278 return;
2279 }
ebc4f76f 2280 $config->{netmask} = $netmask;
d2120e51
DM
2281
2282 $text = $ipconf_entry_gw->get_text();
2283 $text =~ s/^\s+//;
2284 $text =~ s/\s+$//;
b6200603
DM
2285 if (($ipversion == 4) && ($text =~ m!^($IPV4RE)$!)) {
2286 $gateway = $text;
2287 } elsif (($ipversion == 6) && ($text =~ m!^($IPV6RE)$!)) {
d2120e51
DM
2288 $gateway = $text;
2289 } else {
71590b6a 2290 display_message("Gateway is not valid.");
d2120e51
DM
2291 $ipconf_entry_gw->grab_focus();
2292 return;
2293 }
ebc4f76f 2294 $config->{gateway} = $gateway;
1464c7c9 2295
d2120e51
DM
2296 $text = $ipconf_entry_dns->get_text();
2297 $text =~ s/^\s+//;
2298 $text =~ s/\s+$//;
b6200603
DM
2299 if (($ipversion == 4) && ($text =~ m!^($IPV4RE)$!)) {
2300 $dnsserver = $text;
2301 } elsif (($ipversion == 6) && ($text =~ m!^($IPV6RE)$!)) {
d2120e51
DM
2302 $dnsserver = $text;
2303 } else {
71590b6a 2304 display_message("DNS server is not valid.");
d2120e51
DM
2305 $ipconf_entry_dns->grab_focus();
2306 return;
2307 }
ebc4f76f 2308 $config->{dnsserver} = $dnsserver;
1464c7c9 2309
d2120e51 2310 #print "TEST $ipaddress $netmask $gateway $dnsserver\n";
1464c7c9 2311
201a5120 2312 $step_number++;
2e33c3f0 2313 create_ack_view();
89a12446
DM
2314 });
2315
2316 $hostentry->grab_focus();
2317}
2318
2e33c3f0
OB
2319sub create_ack_view {
2320
2321 cleanup_view();
2322
2323 my $ack_template = "${proxmox_libdir}/html/ack_template.htm";
2324 my $ack_html = "${proxmox_libdir}/html/$steps[$step_number]->{html}";
2325 my $html_data = file_get_contents($ack_template);
2326
2327 my %config_values = (
a7d40341 2328 __target_hd__ => join(' | ', @{$config_options->{target_hds}}),
0470018e 2329 __target_fs__ => $config_options->{filesys},
0ddd2227 2330 __country__ => $cmap->{country}->{$country}->{name},
2e33c3f0
OB
2331 __timezone__ => $timezone,
2332 __keymap__ => $keymap,
2333 __mailto__ => $mailto,
2334 __interface__ => $ipconf->{ifaces}->{$ipconf->{selected}}->{name},
2335 __hostname__ => $hostname,
2336 __ip__ => $ipaddress,
2337 __netmask__ => $netmask,
2338 __gateway__ => $gateway,
2339 __dnsserver__ => $dnsserver,
2340 );
2341
2342 while ( my ($k, $v) = each %config_values) {
2343 $html_data =~ s/$k/$v/g;
2344 }
2345
2346 write_config($html_data, $ack_html);
2347
2348 display_html();
2349
2350 set_next(undef, sub {
2351 $step_number++;
2352 create_extract_view();
2353 });
2354}
2355
89a12446
DM
2356sub get_device_desc {
2357 my ($devname, $size, $model) = @_;
2358
d2120e51 2359 if ($size && ($size > 0)) {
1bd457bb 2360 $size = int($size/2048); # size in MB, from 512B "sectors"
89a12446 2361
d2120e51 2362 my $text = "$devname (";
89a12446
DM
2363 if ($size >= 1024) {
2364 $size = int($size/1024); # size in GB
d2120e51 2365 $text .= "${size}GB";
89a12446 2366 } else {
d2120e51 2367 $text .= "${size}MB";
89a12446
DM
2368 }
2369
d2120e51
DM
2370 $text .= ", $model" if $model;
2371 $text .= ")";
2372
89a12446
DM
2373 } else {
2374 return $devname;
2375 }
2376}
2377
2378sub update_layout {
2379 my ($cb, $kmap) = @_;
2380
2381 my $ind;
2382 my $def;
2383 my $i = 0;
2384 my $kmaphash = $cmap->{kmaphash};
2385 foreach my $layout (sort keys %$kmaphash) {
2386 $def = $i if $kmaphash->{$layout} eq 'en-us';
2387 $ind = $i if $kmap && $kmaphash->{$layout} eq $kmap;
2388 $i++;
2389 }
2390
71590b6a 2391 $cb->set_active($ind || $def || 0);
89a12446
DM
2392}
2393
2394my $lastzonecb;
2395sub update_zonelist {
2396 my ($box, $cc) = @_;
2397
2398 my $cczones = $cmap->{cczones};
2399 my $zones = $cmap->{zones};
2400
2401 my $sel;
2402 if ($lastzonecb) {
2403 $sel = $lastzonecb->get_active_text();
2404 $box->remove ($lastzonecb);
2405 } else {
2406 $sel = $timezone; # used once to select default
2407 }
2408
bcbfab6b 2409 my $cb = $lastzonecb = Gtk3::ComboBoxText->new();
71590b6a 2410 $cb->set_size_request(200, -1);
89a12446 2411
71590b6a 2412 $cb->signal_connect('changed' => sub {
89a12446
DM
2413 $timezone = $cb->get_active_text();
2414 });
2415
2416 my @za;
2417 if ($cc && defined ($cczones->{$cc})) {
2418 @za = keys %{$cczones->{$cc}};
2419 } else {
2420 @za = keys %$zones;
2421 }
2422 my $ind;
2423 my $i = 0;
2424 foreach my $zone (sort @za) {
2425 $ind = $i if $sel && $zone eq $sel;
71590b6a 2426 $cb->append_text($zone);
89a12446
DM
2427 $i++;
2428 }
2429
71590b6a 2430 $cb->set_active($ind || 0);
89a12446
DM
2431
2432 $cb->show;
71590b6a 2433 $box->pack_start($cb, 0, 0, 0);
89a12446
DM
2434}
2435
2436sub create_password_view {
2437
71590b6a 2438 cleanup_view();
89a12446 2439
71590b6a
OB
2440 my $vbox2 = Gtk3::VBox->new(0, 0);
2441 $inbox->pack_start($vbox2, 1, 0, 0);
2442 my $vbox = Gtk3::VBox->new(0, 0);
2443 $vbox2->pack_start($vbox, 0, 0, 10);
2444
2445 my $hbox1 = Gtk3::HBox->new(0, 0);
2446 my $label = Gtk3::Label->new("Password");
2447 $label->set_size_request(150, -1);
2448 $label->set_alignment(1, 0.5);
2449 $hbox1->pack_start($label, 0, 0, 10);
2450 my $pwe1 = Gtk3::Entry->new();
2451 $pwe1->set_visibility(0);
201a5120 2452 $pwe1->set_text($password) if $password;
71590b6a
OB
2453 $pwe1->set_size_request(200, -1);
2454 $hbox1->pack_start($pwe1, 0, 0, 0);
2455
2456 my $hbox2 = Gtk3::HBox->new(0, 0);
2457 $label = Gtk3::Label->new("Confirm");
2458 $label->set_size_request(150, -1);
2459 $label->set_alignment(1, 0.5);
2460 $hbox2->pack_start($label, 0, 0, 10);
2461 my $pwe2 = Gtk3::Entry->new();
2462 $pwe2->set_visibility(0);
201a5120 2463 $pwe2->set_text($password) if $password;
71590b6a
OB
2464 $pwe2->set_size_request(200, -1);
2465 $hbox2->pack_start($pwe2, 0, 0, 0);
2466
2467 my $hbox3 = Gtk3::HBox->new(0, 0);
2468 $label = Gtk3::Label->new("E-Mail");
2469 $label->set_size_request(150, -1);
2470 $label->set_alignment(1, 0.5);
2471 $hbox3->pack_start($label, 0, 0, 10);
2472 my $eme = Gtk3::Entry->new();
2473 $eme->set_size_request(200, -1);
201a5120 2474 $eme->set_text($mailto);
71590b6a 2475 $hbox3->pack_start($eme, 0, 0, 0);
89a12446
DM
2476
2477
71590b6a
OB
2478 $vbox->pack_start($hbox1, 0, 0, 5);
2479 $vbox->pack_start($hbox2, 0, 0, 5);
2480 $vbox->pack_start($hbox3, 0, 0, 15);
89a12446
DM
2481
2482 $inbox->show_all;
2483
201a5120 2484 display_html();
89a12446
DM
2485
2486 set_next (undef, sub {
2487
2488 my $t1 = $pwe1->get_text;
2489 my $t2 = $pwe2->get_text;
2490
2491 if (length ($t1) < 5) {
71590b6a 2492 display_message("Password is too short.");
89a12446
DM
2493 $pwe1->grab_focus();
2494 return;
2495 }
2496
2497 if ($t1 ne $t2) {
71590b6a 2498 display_message("Password does not match.");
89a12446
DM
2499 $pwe1->grab_focus();
2500 return;
2501 }
2502
2503 my $t3 = $eme->get_text;
2504 if ($t3 !~ m/^\S+\@\S+\.\S+$/) {
71590b6a 2505 display_message("E-Mail does not look like a valid address" .
89a12446
DM
2506 " (user\@domain.tld)");
2507 $eme->grab_focus();
2508 return;
a39bc1f2 2509 }
89a12446 2510
a39bc1f2 2511 if ($t3 eq 'mail@example.invalid') {
71590b6a 2512 display_message("Please enter a valid E-Mail address");
a39bc1f2
FG
2513 $eme->grab_focus();
2514 return;
89a12446
DM
2515 }
2516
2517 $password = $t1;
2518 $mailto = $t3;
2519
201a5120 2520 $step_number++;
89a12446
DM
2521 create_ipconf_view();
2522 });
2523
2524 $pwe1->grab_focus();
2525
2526}
2527
2528sub create_country_view {
2529
71590b6a 2530 cleanup_view();
89a12446
DM
2531
2532 my $countryhash = $cmap->{countryhash};
2533 my $ctr = $cmap->{country};
2534
71590b6a
OB
2535 my $vbox2 = Gtk3::VBox->new(0, 0);
2536 $inbox->pack_start($vbox2, 1, 0, 0);
2537 my $vbox = Gtk3::VBox->new(0, 0);
2538 $vbox2->pack_start($vbox, 0, 0, 10);
89a12446 2539
71590b6a
OB
2540 my $w = Gtk3::Entry->new();
2541 $w->set_size_request(200, -1);
89a12446 2542
71590b6a
OB
2543 my $c = Gtk3::EntryCompletion->new();
2544 $c->set_text_column(0);
89a12446 2545 $c->set_minimum_key_length(0);
71590b6a
OB
2546 $c->set_popup_set_width(1);
2547 $c->set_inline_completion(1);
2548
2549 my $hbox2 = Gtk3::HBox->new(0, 0);
2550 my $label = Gtk3::Label->new("Time zone");
2551 $label->set_size_request(150, -1);
2552 $label->set_alignment(1, 0.5);
2553 $hbox2->pack_start($label, 0, 0, 10);
89a12446
DM
2554 update_zonelist ($hbox2);
2555
71590b6a
OB
2556 my $hbox3 = Gtk3::HBox->new(0, 0);
2557 $label = Gtk3::Label->new("Keyboard Layout");
2558 $label->set_size_request(150, -1);
2559 $label->set_alignment(1, 0.5);
2560 $hbox3->pack_start($label, 0, 0, 10);
89a12446 2561
bcbfab6b 2562 my $kmapcb = Gtk3::ComboBoxText->new();
89a12446
DM
2563 $kmapcb->set_size_request (200, -1);
2564 foreach my $layout (sort keys %{$cmap->{kmaphash}}) {
2565 $kmapcb->append_text ($layout);
2566 }
2567
71590b6a 2568 update_layout($kmapcb);
89a12446
DM
2569 $hbox3->pack_start ($kmapcb, 0, 0, 0);
2570
2571 $kmapcb->signal_connect ('changed' => sub {
2572 my $sel = $kmapcb->get_active_text();
2573 if (my $kmap = $cmap->{kmaphash}->{$sel}) {
2574 my $xkmap = $cmap->{kmap}->{$kmap}->{x11};
2575 my $xvar = $cmap->{kmap}->{$kmap}->{x11var};
2576 syscmd ("setxkbmap $xkmap $xvar") if !$opt_testmode;
2577 $keymap = $kmap;
2578 }
2579 });
2580
2581 $w->signal_connect ('changed' => sub {
2582 my ($entry, $event) = @_;
2583 my $text = $entry->get_text;
2584
2585 if (my $cc = $countryhash->{lc($text)}) {
71590b6a 2586 update_zonelist($hbox2, $cc);
89a12446 2587 my $kmap = $ctr->{$cc}->{kmap} || 'en-us';
71590b6a 2588 update_layout($kmapcb, $kmap);
89a12446
DM
2589 }
2590 });
2591
2592 $w->signal_connect (key_press_event => sub {
2593 my ($entry, $event) = @_;
2594 my $text = $entry->get_text;
2595
7becc472
DM
2596 my $val = $event->get_keyval;
2597
2598 if ($val == Gtk3::Gdk::KEY_Tab) {
89a12446 2599 my $cc = $countryhash->{lc($text)};
1464c7c9 2600
89a12446
DM
2601 my $found = 0;
2602 my $compl;
7becc472 2603
4443aa27
DM
2604 if ($cc) {
2605 $found = 1;
2606 $compl = $ctr->{$cc}->{name};
2607 } else {
2608 foreach my $cc (keys %$ctr) {
2609 my $ct = $ctr->{$cc}->{name};
2610 if ($ct =~ m/^\Q$text\E.*$/i) {
2611 $found++;
2612 $compl = $ct;
2613 }
2614 last if $found > 1;
89a12446 2615 }
89a12446 2616 }
4443aa27 2617
89a12446 2618 if ($found == 1) {
7becc472 2619 $entry->set_text($compl);
3df718ea 2620 $c->complete();
89a12446
DM
2621 return undef;
2622 } else {
7becc472
DM
2623 #Gtk3::Gdk::beep();
2624 print chr(7); # beep ?
89a12446
DM
2625 }
2626
3df718ea
DM
2627 $c->complete();
2628
7becc472
DM
2629 my $buf = $w->get_buffer();
2630 $buf->insert_text(-1, '', -1); # popup selection
2631
89a12446
DM
2632 return 1;
2633 }
2634
2635 return undef;
2636 });
1464c7c9 2637
7becc472 2638 my $ls = Gtk3::ListStore->new('Glib::String');
89a12446
DM
2639 foreach my $cc (sort {$ctr->{$a}->{name} cmp $ctr->{$b}->{name} } keys %$ctr) {
2640 my $iter = $ls->append();
2641 $ls->set ($iter, 0, $ctr->{$cc}->{name});
2642 }
2643 $c->set_model ($ls);
2644
968fa90b 2645 $w->set_completion ($c);
89a12446 2646
71590b6a 2647 my $hbox = Gtk3::HBox->new(0, 0);
89a12446 2648
71590b6a
OB
2649 $label = Gtk3::Label->new("Country");
2650 $label->set_alignment(1, 0.5);
2651 $label->set_size_request(150, -1);
2652 $hbox->pack_start($label, 0, 0, 10);
2653 $hbox->pack_start($w, 0, 0, 0);
89a12446 2654
71590b6a
OB
2655 $vbox->pack_start($hbox, 0, 0, 5);
2656 $vbox->pack_start($hbox2, 0, 0, 5);
2657 $vbox->pack_start($hbox3, 0, 0, 5);
89a12446 2658
9d1f1ee3 2659 if ($country && $ctr->{$country}) {
89a12446
DM
2660 $w->set_text ($ctr->{$country}->{name});
2661 }
2662
2663 $inbox->show_all;
2664
201a5120 2665 display_html();
89a12446
DM
2666 set_next (undef, sub {
2667
2668 my $text = $w->get_text;
2669
2670 if (my $cc = $countryhash->{lc($text)}) {
2671 $country = $cc;
201a5120 2672 $step_number++;
89a12446
DM
2673 create_password_view();
2674 return;
2675 } else {
71590b6a 2676 display_message("Please select a country first.");
89a12446
DM
2677 $w->grab_focus();
2678 }
2679 });
2680
2681 $w->grab_focus();
2682}
2683
c6ed3b24
DM
2684my $target_hd_combo;
2685my $target_hd_label;
2686
bd3a2e26 2687my $hdoption_first_setup = 1;
c6ed3b24 2688
c7779156
FG
2689my $create_basic_grid = sub {
2690 my $grid = Gtk3::Grid->new();
2691 $grid->set_visible(1);
2692 $grid->set_column_spacing(10);
2693 $grid->set_row_spacing(10);
2694 $grid->set_hexpand(1);
2695
2696 $grid->set_margin_start(5);
2697 $grid->set_margin_end(5);
2698 $grid->set_margin_top(5);
2699 $grid->set_margin_bottom(5);
2700
2701 return $grid;
2702};
2703
2704my $create_label_widget_grid = sub {
2705 my ($labeled_widgets) = @_;
2706
2707 my $grid = &$create_basic_grid();
2708 my $row = 0;
2709
2710 for (my $i = 0; $i < @$labeled_widgets; $i += 2) {
2711 my $widget = @$labeled_widgets[$i+1];
2712 my $label = Gtk3::Label->new(@$labeled_widgets[$i]);
2713 $label->set_visible(1);
2714 $label->set_alignment (1, 0.5);
2715 $grid->attach($label, 0, $row, 1, 1);
2716 $widget->set_visible(1);
2717 $grid->attach($widget, 1, $row, 1, 1);
2718 $row++;
2719 }
2720
2721 return $grid;
2722};
2723
2724my $create_raid_disk_grid = sub {
2725 my $disk_labeled_widgets = [];
2726 for (my $i = 0; $i < @$hds; $i++) {
2727 my $disk_selector = Gtk3::ComboBoxText->new();
2728 $disk_selector->append_text("-- do not use --");
2729 $disk_selector->set_active(0);
2730 $disk_selector->set_visible(1);
2731 foreach my $hd (@$hds) {
2732 my ($disk, $devname, $size, $model) = @$hd;
2733 $disk_selector->append_text(get_device_desc ($devname, $size, $model));
2734 $disk_selector->{pve_disk_id} = $i;
2735 $disk_selector->signal_connect (changed => sub {
2736 my $w = shift;
2737 my $diskid = $w->{pve_disk_id};
2738 my $a = $w->get_active - 1;
2739 $config_options->{"disksel${diskid}"} = ($a >= 0) ? $hds->[$a] : undef;
2740 });
2741 }
2742
bd3a2e26 2743 if ($hdoption_first_setup) {
c7779156
FG
2744 $disk_selector->set_active ($i+1) if $hds->[$i];
2745 } else {
2746 my $hdind = 0;
2747 if (my $cur_hd = $config_options->{"disksel$i"}) {
2748 foreach my $hd (@$hds) {
2749 if (@$hd[1] eq @$cur_hd[1]) {
2750 $disk_selector->set_active($hdind+1);
2751 last;
2752 }
2753 $hdind++;
2754 }
2755 }
2756 }
2757
2758 push @$disk_labeled_widgets, "Harddisk $i", $disk_selector;
2759 }
2760
2761 my $scrolled_window = Gtk3::ScrolledWindow->new();
2762 $scrolled_window->set_hexpand(1);
650a9aab 2763 $scrolled_window->set_propagate_natural_height(1) if @$hds > 4;
c7779156
FG
2764 $scrolled_window->add(&$create_label_widget_grid($disk_labeled_widgets));
2765 $scrolled_window->set_policy('never', 'automatic');
2766
2767 return $scrolled_window;
2768# &$create_label_widget_grid($disk_labeled_widgets)
2769};
2770
d7fe65ff
TL
2771# shared between different ui parts (e.g., ZFS and "normal" single disk FS)
2772my $hdsize_size_adj;
2773my $hdsize_entry_buffer;
2774
2775my $get_hdsize_spinbtn = sub {
2776 my $hdsize = shift;
754abb44 2777
93c8fdb0
SI
2778 $hdsize_entry_buffer //= Gtk3::EntryBuffer->new(undef, 1);
2779
2780 if (defined($hdsize)) {
d7fe65ff 2781 $hdsize_size_adj = Gtk3::Adjustment->new($config_options->{hdsize} || $hdsize, 0, $hdsize+1, 1, 1, 1);
93c8fdb0
SI
2782 } else {
2783 die "called get_hdsize_spinbtn with \$hdsize_size_adj not defined but did not pass hdsize!\n"
2784 if !defined($hdsize_size_adj);
d7fe65ff
TL
2785 }
2786
2787 my $spinbutton_hdsize = Gtk3::SpinButton->new($hdsize_size_adj, 1, 1);
2788 $spinbutton_hdsize->set_buffer($hdsize_entry_buffer);
6039e2f1 2789 $spinbutton_hdsize->set_adjustment($hdsize_size_adj);
d7fe65ff
TL
2790 $spinbutton_hdsize->set_tooltip_text("only use specified size (GB) of the harddisk (rest left unpartitioned)");
2791 return $spinbutton_hdsize;
2792};
2793
c7779156
FG
2794my $create_raid_advanced_grid = sub {
2795 my $labeled_widgets = [];
6c99667a
FG
2796 my $spinbutton_ashift = Gtk3::SpinButton->new_with_range(9,13,1);
2797 $spinbutton_ashift->set_tooltip_text("zpool ashift property (pool sector size, default 2^12)");
2798 $spinbutton_ashift->signal_connect ("value-changed" => sub {
2799 my $w = shift;
2800 $config_options->{ashift} = $w->get_value_as_int();
c7779156
FG
2801 });
2802 $config_options->{ashift} = 12 if ! defined($config_options->{ashift});
6c99667a 2803 $spinbutton_ashift->set_value($config_options->{ashift});
c7779156 2804 push @$labeled_widgets, "ashift";
6c99667a 2805 push @$labeled_widgets, $spinbutton_ashift;
c7779156
FG
2806
2807 my $combo_compress = Gtk3::ComboBoxText->new();
2808 $combo_compress->set_tooltip_text("zfs compression algorithm for rpool dataset");
2809 # note: gzip / lze not allowed for bootfs vdevs
2810 my $comp_opts = ["on","off","lzjb","lz4"];
2811 foreach my $opt (@$comp_opts) {
2812 $combo_compress->append($opt, $opt);
2813 }
2814 $config_options->{compress} = "on" if !defined($config_options->{compress});
2815 $combo_compress->set_active_id($config_options->{compress});
2816 $combo_compress->signal_connect (changed => sub {
2817 my $w = shift;
2818 $config_options->{compress} = $w->get_active_text();
2819 });
2820 push @$labeled_widgets, "compress";
2821 push @$labeled_widgets, $combo_compress;
2822
2823 my $combo_checksum = Gtk3::ComboBoxText->new();
2824 $combo_checksum->set_tooltip_text("zfs checksum algorithm for rpool dataset");
2825 my $csum_opts = ["on", "off","fletcher2", "fletcher4", "sha256"];
2826 foreach my $opt (@$csum_opts) {
2827 $combo_checksum->append($opt, $opt);
2828 }
2829 $config_options->{checksum} = "on" if !($config_options->{checksum});
2830 $combo_checksum->set_active_id($config_options->{checksum});
2831 $combo_checksum->signal_connect (changed => sub {
2832 my $w = shift;
2833 $config_options->{checksum} = $w->get_active_text();
2834 });
2835 push @$labeled_widgets, "checksum";
2836 push @$labeled_widgets, $combo_checksum;
2837
2838 my $spinbutton_copies = Gtk3::SpinButton->new_with_range(1,3,1);
2839 $spinbutton_copies->set_tooltip_text("zfs copies property for rpool dataset (in addition to RAID redundancy!)");
2840 $spinbutton_copies->signal_connect ("value-changed" => sub {
2841 my $w = shift;
2842 $config_options->{copies} = $w->get_value_as_int();
c7779156
FG
2843 });
2844 $config_options->{copies} = 1 if !defined($config_options->{copies});
2845 $spinbutton_copies->set_value($config_options->{copies});
2846 push @$labeled_widgets, "copies", $spinbutton_copies;
2847
d7fe65ff 2848 push @$labeled_widgets, "hdsize", $get_hdsize_spinbtn->();
c7779156
FG
2849 return &$create_label_widget_grid($labeled_widgets);;
2850};
2851
aed81ff0
DM
2852sub create_hdoption_view {
2853
2854 my $dialog = Gtk3::Dialog->new();
2855
2856 $dialog->set_title("Harddisk options");
2857
2858 $dialog->add_button("_OK", 1);
2859
2860 my $contarea = $dialog->get_content_area();
2861
2862 my $hbox2 = Gtk3::Box->new('horizontal', 0);
2863 $contarea->pack_start($hbox2, 1, 1, 10);
2864
2865 my $grid = Gtk3::Grid->new();
2866 $grid->set_column_spacing(10);
2867 $grid->set_row_spacing(10);
1464c7c9 2868
aed81ff0 2869 $hbox2->pack_start($grid, 1, 0, 10);
c6ed3b24
DM
2870
2871 my $row = 0;
2872
aed81ff0
DM
2873 # Filesystem type
2874
71590b6a 2875 my $label0 = Gtk3::Label->new("Filesystem");
aed81ff0 2876 $label0->set_alignment (1, 0.5);
c6ed3b24 2877 $grid->attach($label0, 0, $row, 1, 1);
1464c7c9 2878
bcbfab6b 2879 my $fstypecb = Gtk3::ComboBoxText->new();
aed81ff0 2880
121ebc59
DM
2881 my $fstype = ['ext3', 'ext4', 'xfs',
2882 'zfs (RAID0)', 'zfs (RAID1)',
2883 'zfs (RAID10)', 'zfs (RAIDZ-1)',
6f52fc3d
DM
2884 'zfs (RAIDZ-2)', 'zfs (RAIDZ-3)'];
2885
2886 push @$fstype, 'btrfs (RAID0)', 'btrfs (RAID1)', 'btrfs (RAID10)'
c20d6ab0 2887 if $setup->{enable_btrfs};
aed81ff0 2888
c6ed3b24
DM
2889 my $tcount = 0;
2890 foreach my $tmp (@$fstype) {
2891 $fstypecb->append_text($tmp);
2892 $fstypecb->set_active ($tcount)
2893 if $config_options->{filesys} eq $tmp;
2894 $tcount++;
2895 }
2896
2897 $grid->attach($fstypecb, 1, $row, 1, 1);
2898
2899 $hbox2->show_all();
2900
2901 $row++;
2902
c7779156
FG
2903 my $sep = Gtk3::HSeparator->new();
2904 $sep->set_visible(1);
2905 $grid->attach($sep, 0, $row, 2, 1);
2906 $row++;
aed81ff0 2907
f0a0d90b
TL
2908 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.");
2909 $hw_raid_note->set_line_wrap(1);
2910 $hw_raid_note->set_max_width_chars(30);
f0a0d90b
TL
2911 $hw_raid_note->set_visible(0);
2912 $grid->attach($hw_raid_note, 0, $row++, 2, 1);
2913
c7779156 2914 my $hdsize_labeled_widgets = [];
aed81ff0 2915
c7779156 2916 # size compute
c6ed3b24 2917 my $hdsize = 0;
aed81ff0
DM
2918 if ( -b $target_hd) {
2919 $hdsize = int(hd_size ($target_hd) / (1024*1024.0)); # size in GB
c6ed3b24 2920 } elsif ($target_hd) {
aed81ff0
DM
2921 $hdsize = int((-s $target_hd) / (1024*1024*1024.0));
2922 }
2923
d7fe65ff 2924 my $spinbutton_hdsize = $get_hdsize_spinbtn->($hdsize);
c7779156 2925 push @$hdsize_labeled_widgets, "hdsize", $spinbutton_hdsize;
aed81ff0
DM
2926
2927 my $entry_swapsize = Gtk3::Entry->new();
2928 $entry_swapsize->set_tooltip_text("maximum SWAP size (GB)");
2929 $entry_swapsize->signal_connect (key_press_event => \&check_float);
9bb301fb 2930 $entry_swapsize->set_text($config_options->{swapsize}) if defined($config_options->{swapsize});
c7779156 2931 push @$hdsize_labeled_widgets, "swapsize", $entry_swapsize;
aed81ff0
DM
2932
2933 my $entry_maxroot = Gtk3::Entry->new();
0adc7ca0
DM
2934 if ($setup->{product} eq 'pve') {
2935 $entry_maxroot->set_tooltip_text("maximum size (GB) for LVM root volume");
2936 $entry_maxroot->signal_connect (key_press_event => \&check_float);
2937 $entry_maxroot->set_text($config_options->{maxroot}) if $config_options->{maxroot};
2938 push @$hdsize_labeled_widgets, "maxroot", $entry_maxroot;
2939 }
aed81ff0
DM
2940
2941 my $entry_minfree = Gtk3::Entry->new();
034f75e4 2942 $entry_minfree->set_tooltip_text("minimum free LVM space (GB, required for LVM snapshots)");
aed81ff0 2943 $entry_minfree->signal_connect (key_press_event => \&check_float);
e093944c 2944 $entry_minfree->set_text($config_options->{minfree}) if defined($config_options->{minfree});
c7779156 2945 push @$hdsize_labeled_widgets, "minfree", $entry_minfree;
aed81ff0 2946
b6e875ca
DM
2947 my $entry_maxvz;
2948 if ($setup->{product} eq 'pve') {
2949 $entry_maxvz = Gtk3::Entry->new();
2950 $entry_maxvz->set_tooltip_text("maximum size (GB) for LVM data volume");
2951 $entry_maxvz->signal_connect (key_press_event => \&check_float);
2ba9752e 2952 $entry_maxvz->set_text($config_options->{maxvz}) if defined($config_options->{maxvz});
b6e875ca
DM
2953 push @$hdsize_labeled_widgets, "maxvz", $entry_maxvz;
2954 }
c7779156
FG
2955
2956 my $options_stack = Gtk3::Stack->new();
2957 $options_stack->set_visible(1);
2958 $options_stack->set_hexpand(1);
2959 $options_stack->set_vexpand(1);
2960 $options_stack->add_titled(&$create_raid_disk_grid(), "raiddisk", "Disk Setup");
2961 $options_stack->add_titled(&$create_label_widget_grid($hdsize_labeled_widgets), "hdsize", "Size Options");
2962 $options_stack->add_titled(&$create_raid_advanced_grid("zfs"), "raidzfsadvanced", "Advanced Options");
2963 $options_stack->set_visible_child_name("raiddisk");
2964 my $options_stack_switcher = Gtk3::StackSwitcher->new();
2965 $options_stack_switcher->set_halign('center');
2966 $options_stack_switcher->set_stack($options_stack);
2967 $grid->attach($options_stack_switcher, 0, $row, 2, 1);
2968 $row++;
2969 $grid->attach($options_stack, 0, $row, 2, 1);
c6ed3b24 2970 $row++;
aed81ff0 2971
bd3a2e26 2972 $hdoption_first_setup = 0;
c7779156
FG
2973
2974 my $switch_view = sub {
2975 my $raid = $config_options->{filesys} =~ m/zfs|btrfs/;
2976 my $enable_zfs_opts = $config_options->{filesys} =~ m/zfs/;
c6ed3b24 2977
c7779156
FG
2978 $target_hd_combo->set_visible(!$raid);
2979 $options_stack->get_child_by_name("hdsize")->set_visible(!$raid);
2980 $options_stack->get_child_by_name("raiddisk")->set_visible($raid);
f0a0d90b 2981 $hw_raid_note->set_visible($raid);
c7779156
FG
2982 $options_stack_switcher->set_visible($enable_zfs_opts);
2983 $options_stack->get_child_by_name("raidzfsadvanced")->set_visible($enable_zfs_opts);
2984 if ($raid) {
c6ed3b24 2985 $target_hd_label->set_text("Target: $config_options->{filesys} ");
c7779156 2986 $options_stack->set_visible_child_name("raiddisk");
c6ed3b24 2987 } else {
c6ed3b24
DM
2988 $target_hd_label->set_text("Target Harddisk: ");
2989 }
c7779156
FG
2990 my (undef, $pref_width) = $dialog->get_preferred_width();
2991 my (undef, $pref_height) = $dialog->get_preferred_height();
650a9aab 2992 $pref_height = 750 if $pref_height > 750;
c7779156 2993 $dialog->resize($pref_width, $pref_height);
f7b853d1
DM
2994 };
2995
c7779156 2996 &$switch_view();
f7b853d1
DM
2997
2998 $fstypecb->signal_connect (changed => sub {
2999 $config_options->{filesys} = $fstypecb->get_active_text();
c7779156 3000 &$switch_view();
f7b853d1
DM
3001 });
3002
95844cc6
TL
3003 my $sep2 = Gtk3::HSeparator->new();
3004 $sep2->set_visible(1);
3005 $contarea->pack_end($sep2, 1, 1, 10);
3006
c6ed3b24 3007 $dialog->show();
aed81ff0
DM
3008
3009 $dialog->run();
3010
3011 my $get_float = sub {
3012 my ($entry) = @_;
3013
3014 my $text = $entry->get_text();
3015 return undef if !defined($text);
3016
3017 $text =~ s/^\s+//;
3018 $text =~ s/\s+$//;
3019
3020 return undef if $text !~ m/^\d+(\.\d+)?$/;
3021
3022 return $text;
3023 };
3024
3025 my $tmp;
3026
3027 if (($tmp = &$get_float($spinbutton_hdsize)) && ($tmp != $hdsize)) {
3028 $config_options->{hdsize} = $tmp;
3029 } else {
3030 delete $config_options->{hdsize};
3031 }
3032
3033 if (defined($tmp = &$get_float($entry_swapsize))) {
3034 $config_options->{swapsize} = $tmp;
3035 } else {
3036 delete $config_options->{swapsize};
3037 }
3038
3039 if (defined($tmp = &$get_float($entry_maxroot))) {
3040 $config_options->{maxroot} = $tmp;
3041 } else {
3042 delete $config_options->{maxroot};
3043 }
3044
3045 if (defined($tmp = &$get_float($entry_minfree))) {
3046 $config_options->{minfree} = $tmp;
3047 } else {
3048 delete $config_options->{minfree};
3049 }
3050
b6e875ca 3051 if ($entry_maxvz && defined($tmp = &$get_float($entry_maxvz))) {
aed81ff0
DM
3052 $config_options->{maxvz} = $tmp;
3053 } else {
3054 delete $config_options->{maxvz};
3055 }
3056
3057 $dialog->destroy();
3058}
3059
121ebc59 3060my $get_raid_devlist = sub {
c6ed3b24
DM
3061
3062 my $dev_name_hash = {};
3063
3064 my $devlist = [];
5f8e86d5 3065 for (my $i = 0; $i < @$hds; $i++) {
c6ed3b24
DM
3066 if (my $hd = $config_options->{"disksel$i"}) {
3067 my ($disk, $devname, $size, $model) = @$hd;
1464c7c9 3068 die "device '$devname' is used more than once\n"
c6ed3b24
DM
3069 if $dev_name_hash->{$devname};
3070 $dev_name_hash->{$devname} = $hd;
3071 push @$devlist, $hd;
3072 }
3073 }
3074
121ebc59
DM
3075 return $devlist;
3076};
3077
14aacec8
FG
3078sub zfs_mirror_size_check {
3079 my ($expected, $actual) = @_;
3080
3081 die "mirrored disks must have same size\n"
3082 if abs($expected - $actual) > $expected / 10;
3083}
3084
121ebc59
DM
3085sub get_zfs_raid_setup {
3086
3087 my $filesys = $config_options->{filesys};
3088
3089 my $devlist = &$get_raid_devlist();
3090
224bb7b0 3091 my $diskcount = scalar(@$devlist);
0cfa502c 3092 die "$filesys needs at least one device\n" if $diskcount < 1;
c6ed3b24 3093
121ebc59
DM
3094 my $bootdevlist = [];
3095
c6ed3b24
DM
3096 my $cmd= '';
3097 if ($filesys eq 'zfs (RAID0)') {
3098 push @$bootdevlist, @$devlist[0];
3099 foreach my $hd (@$devlist) {
3100 $cmd .= " @$hd[1]";
3101 }
3102 } elsif ($filesys eq 'zfs (RAID1)') {
0cfa502c 3103 die "zfs (RAID1) needs at least 2 device\n" if $diskcount < 2;
c6ed3b24 3104 $cmd .= ' mirror ';
269c66a6 3105 my $hd = @$devlist[0];
14aacec8 3106 my $expected_size = @$hd[2]; # all disks need approximately same size
269c66a6 3107 foreach $hd (@$devlist) {
14aacec8 3108 zfs_mirror_size_check($expected_size, @$hd[2]);
c6ed3b24
DM
3109 $cmd .= " @$hd[1]";
3110 push @$bootdevlist, $hd;
3111 }
3112 } elsif ($filesys eq 'zfs (RAID10)') {
0cfa502c 3113 die "zfs (RAID10) needs at least 4 device\n" if $diskcount < 4;
b8f4f0f9 3114 die "zfs (RAID10) needs an even number of devices\n" if $diskcount & 1;
1464c7c9 3115
c6ed3b24
DM
3116 push @$bootdevlist, @$devlist[0], @$devlist[1];
3117
224bb7b0 3118 for (my $i = 0; $i < $diskcount; $i+=2) {
c6ed3b24
DM
3119 my $hd1 = @$devlist[$i];
3120 my $hd2 = @$devlist[$i+1];
14aacec8 3121 zfs_mirror_size_check(@$hd1[2], @$hd2[2]); # pairs need approximately same size
c6ed3b24
DM
3122 $cmd .= ' mirror ' . @$hd1[1] . ' ' . @$hd2[1];
3123 }
3124
3125 } elsif ($filesys =~ m/^zfs \(RAIDZ-([123])\)$/) {
3126 my $level = $1;
3127 my $mindisks = 2 + $level;
0cfa502c 3128 die "zfs (RAIDZ-$level) needs at least $mindisks devices\n" if scalar(@$devlist) < $mindisks;
269c66a6 3129 my $hd = @$devlist[0];
14aacec8 3130 my $expected_size = @$hd[2]; # all disks need approximately same size
097ecf8f 3131 $cmd .= " raidz$level";
269c66a6 3132 foreach $hd (@$devlist) {
14aacec8 3133 zfs_mirror_size_check($expected_size, @$hd[2]);
c6ed3b24
DM
3134 $cmd .= " @$hd[1]";
3135 push @$bootdevlist, $hd;
3136 }
3137 } else {
3138 die "unknown zfs mode '$filesys'\n";
3139 }
3140
3141 return ($devlist, $bootdevlist, $cmd);
3142}
3143
121ebc59
DM
3144sub get_btrfs_raid_setup {
3145
3146 my $filesys = $config_options->{filesys};
3147
3148 my $devlist = &$get_raid_devlist();
3149
3150 my $diskcount = scalar(@$devlist);
0cfa502c 3151 die "$filesys needs at least one device\n" if $diskcount < 1;
121ebc59
DM
3152
3153 my $mode;
3154
3155 if ($diskcount == 1) {
3156 $mode = 'single';
3157 } else {
3158 if ($filesys eq 'btrfs (RAID0)') {
3159 $mode = 'raid0';
3160 } elsif ($filesys eq 'btrfs (RAID1)') {
0cfa502c 3161 die "btrfs (RAID1) needs at least 2 device\n" if $diskcount < 2;
121ebc59
DM
3162 $mode = 'raid1';
3163 } elsif ($filesys eq 'btrfs (RAID10)') {
0cfa502c 3164 die "btrfs (RAID10) needs at least 4 device\n" if $diskcount < 4;
121ebc59
DM
3165 $mode = 'raid10';
3166 } else {
9d69f3d3 3167 die "unknown btrfs mode '$filesys'\n";
121ebc59
DM
3168 }
3169 }
3170
3171 return ($devlist, $mode);
3172}
3173
218a4b6b 3174my $last_hd_selected = 0;
89a12446
DM
3175sub create_hdsel_view {
3176
451b1da5 3177 $prev_btn->set_sensitive(1); # enable previous button at this point
201a5120 3178
71590b6a 3179 cleanup_view();
89a12446 3180
71590b6a
OB
3181 my $vbox = Gtk3::VBox->new(0, 0);
3182 $inbox->pack_start($vbox, 1, 0, 0);
3183 my $hbox = Gtk3::HBox->new(0, 0);
3184 $vbox->pack_start($hbox, 0, 0, 10);
968fa90b 3185
89a12446 3186 my ($disk, $devname, $size, $model) = @{@$hds[0]};
9227a70f 3187 $target_hd = $devname if !defined($target_hd);
89a12446 3188
71590b6a
OB
3189 $target_hd_label = Gtk3::Label->new("Target Harddisk: ");
3190 $hbox->pack_start($target_hd_label, 0, 0, 0);
89a12446 3191
bcbfab6b 3192 $target_hd_combo = Gtk3::ComboBoxText->new();
89a12446 3193
1aa5bd02
DM
3194 foreach my $hd (@$hds) {
3195 ($disk, $devname, $size, $model) = @$hd;
71590b6a 3196 $target_hd_combo->append_text (get_device_desc($devname, $size, $model));
1aa5bd02 3197 }
89a12446 3198
90af1603
OB
3199 my $raid = $config_options->{filesys} =~ m/zfs|btrfs/;
3200 if ($raid) {
3201 $target_hd_label->set_text("Target: $config_options->{filesys} ");
3202 $target_hd_combo->set_visible(0);
3203 $target_hd_combo->set_no_show_all(1);
3204 }
218a4b6b 3205 $target_hd_combo->set_active($last_hd_selected);
71590b6a 3206 $target_hd_combo->signal_connect(changed => sub {
1aa5bd02
DM
3207 $a = shift->get_active;
3208 my ($disk, $devname) = @{@$hds[$a]};
3b959bef 3209 $last_hd_selected = $a;
1aa5bd02 3210 $target_hd = $devname;
1aa5bd02 3211 });
1464c7c9 3212
71590b6a 3213 $hbox->pack_start($target_hd_combo, 0, 0, 10);
aed81ff0 3214
71590b6a 3215 my $options = Gtk3::Button->new('_Options');
aed81ff0
DM
3216 $options->signal_connect (clicked => \&create_hdoption_view);
3217 $hbox->pack_start ($options, 0, 0, 0);
3218
89a12446
DM
3219
3220 $inbox->show_all;
3221
201a5120 3222 display_html();
c6ed3b24 3223
71590b6a 3224 set_next(undef, sub {
c6ed3b24
DM
3225
3226 if ($config_options->{filesys} =~ m/zfs/) {
a7d40341 3227 my ($devlist) = eval { get_zfs_raid_setup() };
c6ed3b24 3228 if (my $err = $@) {
303dfb2c
TL
3229 display_message("Warning: $err\nPlease fix ZFS setup first.");
3230 return;
c6ed3b24 3231 }
303dfb2c 3232 $config_options->{target_hds} = [ map { $_->[1] } @$devlist ];
121ebc59 3233 } elsif ($config_options->{filesys} =~ m/btrfs/) {
a7d40341 3234 my ($devlist) = eval { get_btrfs_raid_setup() };
121ebc59 3235 if (my $err = $@) {
303dfb2c
TL
3236 display_message("Warning: $err\nPlease fix BTRFS setup first.");
3237 return;
121ebc59 3238 }
303dfb2c 3239 $config_options->{target_hds} = [ map { $_->[1] } @$devlist ];
c6ed3b24 3240 } else {
a7d40341 3241 $config_options->{target_hds} = [ $target_hd ];
c6ed3b24 3242 }
303dfb2c
TL
3243
3244 $step_number++;
3245 create_country_view();
c6ed3b24 3246 });
89a12446
DM
3247}
3248
3249sub create_extract_view {
3250
71590b6a 3251 cleanup_view();
89a12446 3252
550958aa
DM
3253 display_info();
3254
201a5120 3255 $next->set_sensitive(0);
ac3ee85b
TL
3256 $prev_btn->set_sensitive(0);
3257 $prev_btn->hide();
89a12446 3258
71590b6a 3259 my $vbox = Gtk3::VBox->new(0, 0);
89a12446 3260 $inbox->pack_start ($vbox, 1, 0, 0);
71590b6a 3261 my $hbox = Gtk3::HBox->new(0, 0);
53986d77 3262 $vbox->pack_start ($hbox, 0, 0, 10);
89a12446 3263
71590b6a 3264 my $vbox2 = Gtk3::VBox->new(0, 0);
89a12446
DM
3265 $hbox->pack_start ($vbox2, 0, 0, 0);
3266
71590b6a 3267 $progress_status = Gtk3::Label->new('');
89a12446 3268 $vbox2->pack_start ($progress_status, 1, 1, 0);
968fa90b 3269
7becc472 3270 $progress = Gtk3::ProgressBar->new;
45feca6f 3271 $progress->set_show_text(1);
7becc472 3272 $progress->set_size_request (600, -1);
89a12446 3273
71590b6a 3274 $vbox2->pack_start($progress, 0, 0, 0);
89a12446 3275
201a5120 3276 $inbox->show_all();
89a12446
DM
3277
3278 my $tdir = $opt_testmode ? "target" : "/target";
3279 mkdir $tdir;
97980bf2 3280 my $base = "${proxmox_cddir}/$setup->{product}-base.squashfs";
89a12446 3281
71590b6a 3282 eval { extract_data($base, $tdir); };
89a12446
DM
3283 my $err = $@;
3284
201a5120 3285 $next->set_sensitive(1);
89a12446 3286
71590b6a 3287 set_next("_Reboot", sub { exit (0); } );
89a12446 3288
296cf41f 3289 if ($err) {
201a5120
OB
3290 display_html("fail.htm");
3291 display_error($err);
296cf41f 3292 } else {
201a5120
OB
3293 cleanup_view();
3294 display_html("success.htm");
296cf41f 3295 }
89a12446
DM
3296}
3297
89a12446
DM
3298sub create_intro_view {
3299
451b1da5 3300 $prev_btn->set_sensitive(0);
201a5120
OB
3301
3302 cleanup_view();
89a12446 3303
ca951e77 3304 if (int($total_memory) < 1024) {
18a9811e
TL
3305 my $fullname = $product_fullname->{$setup->{product}};
3306
3befbf97 3307 display_error("Less than 1 GiB of usable memory detected, installation will probably fail.\n\n".
18a9811e 3308 "See 'System Requirements' in the $fullname documentation.");
2b85ee1b
OB
3309 }
3310
bdeca872
DM
3311 if ($setup->{product} eq 'pve') {
3312 eval {
3313 my $cpuinfo = file_get_contents('/proc/cpuinfo');
3314 if ($cpuinfo && !($cpuinfo =~ /^flags\s*:.*(vmx|svm)/m)) {
2780ea4f 3315 display_error("No support for KVM virtualization detected.\n\n" .
bdeca872
DM
3316 "Check BIOS settings for Intel VT / AMD-V / SVM.")
3317 }
3318 };
3319 }
7fff0d85 3320
201a5120 3321 display_html();
89a12446 3322
201a5120 3323 $step_number++;
71590b6a 3324 set_next("I a_gree", \&create_hdsel_view);
89a12446
DM
3325}
3326
71590b6a 3327$ipconf = get_ip_config();
89a12446 3328
9d1f1ee3 3329$country = detect_country() if $ipconf->{default} || $opt_testmode;
89a12446
DM
3330
3331# read country, kmap and timezone infos
71590b6a 3332$cmap = read_cmap();
89a12446 3333
9d1f1ee3
FG
3334if (!defined($cmap->{country}->{$country})) {
3335 print $logfd "ignoring detected country '$country', invalid or unknown\n";
3336 $country = undef;
3337}
3338
89a12446
DM
3339create_main_window ();
3340
ff2ce71c
FG
3341my $initial_error = 0;
3342
89a12446
DM
3343if (!defined ($hds) || (scalar (@$hds) <= 0)) {
3344 print "no hardisks found\n";
ff2ce71c 3345 $initial_error = 1;
201a5120 3346 display_html("nohds.htm");
71590b6a 3347 set_next("Reboot", sub { exit(0); } );
89a12446 3348} else {
89a12446
DM
3349 foreach my $hd (@$hds) {
3350 my ($disk, $devname) = @$hd;
3351 next if $devname =~ m|^/dev/md\d+$|;
3352 print "found Disk$disk N:$devname\n";
3353 }
89a12446
DM
3354}
3355
72836708
FG
3356if (!$initial_error && (scalar keys %{ $ipconf->{ifaces} } == 0)) {
3357 print "no network interfaces found\n";
3358 $initial_error = 1;
201a5120 3359 display_html("nonics.htm");
71590b6a 3360 set_next("Reboot", sub { exit(0); } );
72836708
FG
3361}
3362
ff2ce71c
FG
3363create_intro_view () if !$initial_error;
3364
7becc472 3365Gtk3->main;
89a12446
DM
3366
3367exit 0;