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